-
15Java进阶 进程
t.join():让主线程进入线程池,等待t执行完才执行。
t.sleep():让线程阻塞,休眠一段时间,休眠结束后进入就绪状态。不会释放锁。
t.yield():让线程让出CPU,从运行态进入就绪态。可能会接着进入运行态。
t.setDaemon():设置为守护线程,非守护线程都死了的时候自动终止。
2 线程的分类
线程分为:守护线程 用户线程
-
守护线程和用户线程基本上是相同的,唯一区别就是判断JVM何时离开
-
守护线程是用来服务用户线程的。通过在start方法之前调用thread.setDaemon(true) 可以将一个用户线程变成守护线程
-
java的垃圾回收 他是一个典型的守护线程
-
如果JVM中都是守护线程,JVM将退出(用户线程执行结束 守护线程无论是否结束 都将终止执行)
3 线程的生命周期
jdk中用Thread State定义了线程的状态:
-
线程状态。线程可以处于以下状态之一:
线程的状态通常分为5种状态:
新建:当一个Thread类及其子类的对象被声明并创建时,此时的线程对象就处于新建状态
就绪 : 处于新建状态的线程被start后,线程将进入CPU的执行队列等待获得CPU的执行权,此时的线程已经具备了运行的条件,只是还有获得CPU的执行权
运行:当就绪的线程获得CPU的执行权 ,处于运行状态
阻塞:在某种特殊的情况下,被人为挂起或执行输入输出操作时,让出CPU并临时中止自己的执行。此时线程就进入阻塞状态
死亡:线程完成了他的全部工作或被线程被提前强制性的终止 或出现异常导致线程异常结束。
线程状态之间的相互转换
-
一个线程一旦死亡 是不可以在重新启动的。
4.线程同步
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st = new SellTicket();
//创建三个线程
Thread t1 = new Thread(st,"1号窗口");
Thread t2 = new Thread(st,"2号窗口");
Thread t3 = new Thread(st,"3号窗口");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
public class SellTicket implements Runnable{
private int ticktes = 100;
@Override
public void run() {
while(true){
if(ticktes > 0 ){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"张票");//会出现数据不同步的现象,与实际需求不符
ticktes--;
}
}
}
}
4.1 解决数据安全问题--同步代码块
出现问题的条件:
1 多线程环境
2 有共享数据
3 有多条语句操作共享数据
如何解决线程安全问题:
基本的思想:让程序没有安全问题的环境
怎么实现呢? 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行。
java提供了解决的方式是使用同步代码块或同步方法:synchronized 相当于给代码加锁
可以用在代码块和方法上 分别称为同步代码块和同步方法:
代码块:synchronized(obj/this){
共享代码;
}
在同步代码块中 谁来担当这个所对象呢?
任意对象都可以充当所对象 一般情况下使用this
4.2 解决数据安全问题--同步方法
在方法的声明上添加synchronized关键字
同步成员方法
静态同步方法:static synchronized
静态方法的同步代码块:使用类名.class作为锁对象
静态的同步方法或者静态方法中的同步代码块的所对象是类名.class对象
单例设计模式的懒汉式的线程安全问题
public class Singleton {
private static Singleton instance;
private Singleton(){
}
public static Singleton getInstance(){
if( instance == null){
synchronized (Singleton.class){
if(instance ==null){
instance = new Singleton();
}
}
}
return instance;
}
}
4.6 线程安全的类
StringBuffer 线程安全的可变字符序列
StringBuilder 线程不安全的可变字符序列
Vector 线程同步的
HashTable 线程同步的 是一个哈希表的实现
在实际使用时,如果不需要线程安全的实现,推荐使用与之功能相同的 但是线程不同步的实现
5 线程的死锁的演示
死锁是我们需要规避的问题
不同的线程分别占用对方所需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就行成了线程死锁
出现死锁 不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态 无法继续
死锁问题的出现是一个概率时间。
死锁问题的解决:
1 减少同步代码块或同步方法的嵌套
2 尽量减少同步资源的定义
3 使用专门的算法
提示:
1 明确那些代码是多线程运行的代码,就是需要写入run方法
2 明确那些数据是共享数据
3 明确多下称运行代码中的那些语句操作了共享数据
6 线程通信
6.1 什么时候需要线程通信
多个线程并发执行,在默认情况下CPU随机切换线程,如果我们希望他们有规律的执行,就需要使用线程通信。
6.2 线程间如何通信
如果线程需要等待 就调用的wait().wait()方法可以等待时间结束或者被唤醒。如果要唤醒一个等待的线程 那么就使用notify() /notifyAll()
6.3 互斥锁
互斥锁 依次最多只能有一个线程持有锁
锁是用于通过多个线程控制对共享资源的访问的工具。 通常,锁提供对共享资源的独占访问:一次只能有一个线程可以获取锁,并且对共享资源的所有访问都要求首先获取锁
lock.lock():加锁 lock.unlock()解锁
Interface Lock 可以使用的实现类 ReentrantLock
-
一个可重入互斥
Condition
c1.await():使当前线程等待,直到c1.singal()或者interrupt()打断阻塞状态,或者到await()指定的时间。
c1.signal():唤醒一个等待线程
c1.signalAll():唤醒所有等待进程
不同的线程需要使用不同的 Condition 这样就能区分唤醒额时候唤醒的是那个线程
public class ThreadLock {
public static void main(String[] args) {
ThreadLock tw = new ThreadLock();
new Thread(new Runnable() {
@Override
public void run() {
while(true){
tw.print1();
}
}
}).start();
new Thread(){
@Override
public void run() {
while(true){
tw.print2() ;
}
}
}.start();
new Thread(){
@Override
public void run() {
while(true){
tw.print3() ;
}
}
}.start();
}
// 创建Lock锁对象
Lock lock = new ReentrantLock();
//创建锁使用的条件
Condition c1 = lock.newCondition();
Condition c2 = lock.newCondition();
Condition c3 = lock.newCondition();
// 创建一个唤醒标志
int flag = 1;
public void print1(){
lock.lock();//给当前代码上锁
if(flag != 1){
try {
c1.await();//当前线程处于等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("中");
System.out.print("北");
System.out.print("大");
System.out.print("学");
System.out.println();
flag = 2;
c2.signal();//唤醒c2
lock.unlock();//释放锁
}
public void print2(){
lock.lock();
if(flag != 2){
try {
c2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("塔");
System.out.print("里");
System.out.print("木");
System.out.print("大");
System.out.print("学");
System.out.println();
flag =3;
c3.signal();
lock.unlock();
}
public void print3(){
lock.lock();
if(flag != 3){
try {
c3.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.print("青");
System.out.print("岛");
System.out.print("大");
System.out.print("学");
System.out.println();
flag =1;
c1.signal();
lock.unlock();
}
}
6.4生产者—消费者模型
生产者消费者模型的作用是什么?
1通过平衡生产者的生产能力和消费者消费能力来提升整个系统的运行效率
2 解耦
多线程的学习重点:
1 线程的创建方式
2线程的生命周期(线程的五种状态的转换)
3 线程同步
4 线程通信
出处:https://www.cnblogs.com/Justhis610/p/15083209.html