线程的实现方式
学习视频地址
bilibili
Thread VS Runnable
Synchronized
Thread
1、使用多线程要继承Thread类
2、重写run()方法,编写线程执行体
3、创建线程对象,调用start()方法启动线程
Runnable
创建线程方法的第二种:Runable
- 用implements实现 Runable 接口
- 重写run方法
- 执行线程需要丢入runable接口实现类,调用start方法
Thread VS Runnable
两种方式对比
应用Thread模拟买票
class MyThread extends Thread{
private int ticketsCont=5; //一共5张火车票
private String name; //窗口名字
public MyThread(String name){
this.name=name;
}
@Override
public void run() {
while(ticketsCont>0){
ticketsCont--;
System.out.println(name+"卖了一张票,还剩"+ticketsCont);
}
}
}
public class TicketsThread{
public static void main(String[] args) {
//创建三个线程,模拟三个窗口卖票
MyThread mt1 = new MyThread("窗口1");
MyThread mt2 = new MyThread("窗口2");
MyThread mt3 = new MyThread("窗口3");
mt1.start();
mt2.start();
mt3.start();
}
}
运行结果:
应用Runnable模拟卖票
class MyThread implements Runnable{
private int ticketsCont=5; //一共5张火车票
@Override
public void run() {
while(ticketsCont>0){
ticketsCont--;
System.out.println(Thread.currentThread().getName()+"卖了一张票,还剩"+ticketsCont);
}
}
}
public class TicketsRunnale {
public static void main(String[] args){
MyThread mt =new MyThread();
Thread th1 = new Thread(mt,"窗口1");
Thread th2 = new Thread(mt,"窗口2");
Thread th3 = new Thread(mt,"窗口3");
//启动
th1.start();
th2.start();
th3.start();
}
}
运行结果:
差异分析:
我们来分析Runnable实现
Thread实现:
小结:
- Runnable方式可以避免Thread方式由于java单继承特性带来的缺陷
- Runnable的代码可以被多个线程(Thread实例)共享,适合于多个线程处理同一资源的情况
线程状态
停止线程
- 不推荐使用JDK提供的stop()、destroy()方法
- 推荐线程自己停止下来
- 建议使用一个标志位进行终止变量,当flag=false,则终止线程运行
public class TestStop implements Runnable{
//1、设置一个标识位
private boolean flag=true;
@Override
public void run() {
int i =0;
while (flag){
System.out.println("run....Thread"+i++);
}
}
public void stop(){
this.flag=false;
}
public static void main(String[] args) {
TestStop testStop = new TestStop();
new Thread(testStop).start();
for(int i=0;i<1000;i++){
System.out.println("main..."+i);
if(i==900){
//调用stop方法切换标志位,让线程停止
testStop.stop();
System.out.println("该线程停止");
}
}
}
}
线程的生命周期
线程礼让(yield)
- 礼让线程,让当前正在执行的线程暂停,但不堵塞
- 将线程从运行状态转为就绪状态
- 让cpu重新调度,礼让不一定成功!看CPU心情
实例:
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"shui").start();
new Thread(myYield,"zhan").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
执行结果:
线程强制执行(join)
- Join合并线程,待线程执行完成后,再执行其他线程,其他线程阻塞
- 可以想象成插队
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9IDpy33z-1586503736879)(img/image-20200410123950571.png)]
实例:
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i=0;i<1000;i++){
System.out.println("线程VIP来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//主线程
for(int i=0;i<500;i++){
if(i==200){
thread.join();
}
System.out.println("main"+i);
}
}
}
wait和notify方法
-
wait():等待,如果线程执行了wait方法,那么该线程会进入等待的状态,等待状态下的线程必须要被其他线程调用notify()方法才能唤醒。
-
notify():唤醒,唤醒线程池等待线程其中的一个。
-
notifyAll():唤醒线程池所有等待线程。
wait与notify方法要注意的事项:
-
wait方法与notify方法是属于Object对象的。
-
wait方法与notify方法必须要在同步代码块或者是同步函数中才能使用。
-
wait方法与notify方法必须要由所对象调用。
守护线程
java线程有两类
用户线程
- 运行在前台,执行具体的任务
- 程序的主线程、连接网络的子线程等都是用户线程
守护线程
- 运行在后台,为其他前台线程服务
- 特点:一旦所有用户线程都结束运行,守护线程会随JVM一起结束
- 应用:
- 数据库连接池中的监测线程
- JVM虚拟机启动后的监测线程
- 常见:垃圾回收线程
如何设置守护线程
可以通过调用Thread类的setDeamon(true)方法来设置当前的线程为守护线程
注意事项
- setDaemon(true)必须在start()方法之前调用,否则会抛出IllegalThreadStateException异常
- 在守护线程中产生的新线程也是守护线程
- 不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑
守护线程实例代码
class DaemonThread implements Runnable{
@Override
public void run() {
System.out.println("进入守护线程"+Thread.currentThread().getName());
try {
writeToFile();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("退出守护线程"+Thread.currentThread().getName());
}
private void writeToFile() throws Exception{
File filename=new File("D:"+File.separator+"daemon.txt");
OutputStream os =new FileOutputStream(filename,true);
int count=0;
while(count<100){
os.write(("\r\nword"+count).getBytes());
System.out.println("守护线程"+Thread.currentThread().getName()+"向文件中写入了word"+count++);
//线程睡眠(毫秒)
Thread.sleep(1000);
}
}
}
public class DeamonThreadDemo {
public static void main(String[] arg){
System.out.println("进入主线程"+Thread.currentThread().getName());
DaemonThread daemonThread=new DaemonThread();
Thread thread=new Thread(daemonThread);
thread.setDaemon(true);
thread.start();
//键盘控制终止
Scanner sc =new Scanner(System.in);
sc.next();
System.out.println("退出主线程"+Thread.currentThread().getName());
}
}
死锁
- 多个线程各自占有一些共享资源,并且互相等待其他线程占有的资源才能运行,而导致两个或多个线程都在等待对方释放资源,都停止执行的情形。
- 某一个同步块同时拥有
两个以上对象的锁
时,就可能发生死锁
的问题
死锁的避免方法
-
产生死锁的四个必要条件
- 互斥条件:一个资源每次只能被一个进程使用
- 请求与保持条件:一个进程因请求资源而阻塞,对一获得的资源保持不放
- 不剥夺条件:进程已获得的资源,在未使用完之前,不能强行剥夺
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源的关系
上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或者多个条件就能避免死锁的发生
Synchronized
Synchronized简介
作用
能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果。
地位
- Synchronized是java的关键字,被java语言原生支持
- 是最基本的互斥同步手段
- 是并发编程中的元老级角色,是并发编程的必学内容
不适用并发手段的后果
实例:
/*
* 消失的请求数
* */
public class DisappearRequest1 implements Runnable{
static DisappearRequest1 instance=new DisappearRequest1();
static int i=0;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(i);
}
@Override
public void run() {
for(int j=0;j<100000;j++){
i++;
}
}
}
执行结果:152152
原因:
i++,看上去只是一个操作,实际上包含了三个动作:
1、读取 i
2、将i+1
3、将i的值写入到内存中
Synchronized两种用法
- 对象锁
- 包含方法锁(默认锁对象为this当前实例对象)
- 同步代码块锁(自己指定对象)
- 类锁
- 指Synchronized修饰静态的方法
- 指定锁为Class对象
对象锁
代码块形式
lock为自己指定对象锁
public class SynchronizedObjectCodeBlock2 implements Runnable{
static SynchronizedObjectCodeBlock2 instance=new SynchronizedObjectCodeBlock2();
Object lock=new Object();
@Override
public void run() {
synchronized (this){
System.out.println("我是对象锁,我叫"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
}
synchronized (lock){
System.out.println("我是对象锁,我叫"+Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
}
}
public static void main(String[] args) {
Thread t1=new Thread(instance);
Thread t2 = new Thread(instance);
t1.start();
t2.start();
while(t1.isAlive() || t2.isAlive()){
}
System.out.println("finished");
}
}
方法所形式
public class SynchronizedObjectMethod3 implements Runnable {
static SynchronizedObjectMethod3 instance =new SynchronizedObjectMethod3();
public static void main(String[] args) {
Thread t2=new Thread(instance);
Thread t3 = new Thread(instance);
t2.start();
t3.start();
while(t2.isAlive() || t3.isAlive()){
}
System.out.println("finished");
}
@Override
public void run() {
method();
}
public synchronized void method(){
System.out.println("我是对象锁,我叫"+Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"运行结束");
}
}
类锁
-
概念(重要):Java类可能有很多歌对象,但只有1个Class对象
- 本质:所谓的类锁,不过是Class对象的锁而已
- 用法和效果:类锁只能同一时刻被一个对象拥有
-
形式1:synchronized加在static方法中
public class SynchronizedClassStatic4 implements Runnable { static SynchronizedClassStatic4 instance1=new SynchronizedClassStatic4(); static SynchronizedClassStatic4 instance2=new SynchronizedClassStatic4(); @Override public void run() { method(); } public static synchronized void method(){ System.out.println("我是对象锁,我叫"+Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"运行结束"); } public static void main(String[] args) { Thread t2=new Thread(instance1); Thread t3 = new Thread(instance2); t2.start(); t3.start(); while(t2.isAlive() || t3.isAlive()){ } System.out.println("finished"); } }
-
形式2:synchronized(*.class)代码块
public class SynchronizedClass5 implements Runnable { static SynchronizedClass5 instance1=new SynchronizedClass5(); static SynchronizedClass5 instance2=new SynchronizedClass5(); @Override public void run() { method(); } private void method(){ synchronized (SynchronizedClass5.class){ System.out.println("我是类锁的第二种形式,(*.class)。我叫:"+Thread.currentThread().getName()); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"运行结束"); } } public static void main(String[] args) { Thread t2=new Thread(instance1); Thread t3 = new Thread(instance2); t2.start(); t3.start(); while(t2.isAlive() || t3.isAlive()){ } System.out.println("finished"); } }
消失请求的解决方案
方法一:
public synchronized void run() { for(int j=0;j<100000;j++){ i++; } }
方法二:
@Override public void run() { synchronized(this) { for (int j = 0; j < 100000; j++) { i++; } } } }
方法三:
@Override public void run() { synchronized(DisappearRequest1.class) { for (int j = 0; j < 100000; j++) { i++; } } }