Java语法基础学习DaySeventeen(多线程续)
一、线程的特点
1.线程的分类
java中的线程分为两类:守护线程和用户线程。唯一的区别是判断JVM何时离开。
守护线程是用来服务用户线程的,通过在start()方法前调用Thread.setDaemon(true)可以把一个用户线程变成一个守护线程。
java垃圾回收就是一个典型的守护线程;若JVM中都是守护线程,当前JVM将退出。
2.线程的生命周期

3.线程的同步机制
(1)线程的安全问题
在上一篇随笔中,多窗口售票的程序代码存在线程的安全问题,打印车票会出现重票、错票。
安全问题存在的原因:
由于一个线程在操作共享数据时,未执行完毕的情况下就有另外的线程参与进来,导致共享数据存在了安全问题。
解决方法:
让一个线程操作共享数据完毕后,其他线程才有机会参与共享数据的操作,即采用同步机制。
(2)方法一:同步代码块
synchronized(同步监视器){
    //需要被同步的代码块(即为操作共享数据的代码)
}
同步监视器:由任意一个类的对象来充当,哪个线程获取此监视器,谁就执行大括号里被同步的代码,俗称:锁。
要求:
所有线程必须共用同一把锁。
在实现的方式中可以用this来当同步锁,在继承的方式中慎用this,因为this将指向不同的对象。
多窗口售票代码案例
class Window implements Runnable{
    int ticket = ;//共享数据
    Object obj = new Object();//创建一个对象,作为同步锁。
    public void run(){
        //Object obj = new Object();注意不能在这里创建,局部变量起不到同步控制的作用。
        while(true){
            synchronized(obj){//同步控制,obj作为锁,也可以用this
                if(ticket > ){
                    System.out.println(Thread.currentThread().getName()+"售票,票号为:" + ticket--);
                }else{
                    break;
                }
            }
        }
    }
}
(3)方法二:同步方法
将操作共享数据的方法声明为synchronized,能够保证当一个线程执行此方法时,其他线程在外等待直至此线程执行完此方法。
同步锁:this(即调用该方法的对象,所以只适用实现的多线程方法,不适用于继承的多线程方法)
多窗口售票代码案例
class Window implements Runnable{
    int ticket = ;//共享数据
    public void run(){
        while(true) {
            show();
        }
    }
    public synchronized void show() {
        if (ticket > ) {
            try {
                Thread.currentThread().sleep();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "售票,票号为:" + ticket--);
        }
    }
}
(4)单例模式懒汉式的线程安全问题:使用同步机制
注意:对于一般的方法可以使用this当同步锁;但对于静态方法就没办法使用this,而是使用当前类本身充当锁。
public class Singleton {
    private Singleton(){
    }
    private static Singleton instance = null;
    public static Singleton getInstance() {
        synchronized (Singleton.class) {//注意静态方法的同步锁使用类本身
            if (instance == null) {
                instance = new Singleton();
            }
        }
        return instance;
    }
}
(5)释放同步锁的操作:
同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁。
(6)不释放锁的操作:
Thread.sleep()、Thread.yield()暂停当前线程的执行,但不会释放锁。
(7)线程同步练习
有两个储户分别向同一个账户存3000元,每次存1000元,每次存完打印账户余额。(存在线程安全问题)
class Account{
    double balance;
    public Account() {
    }
    //存钱
    public synchronized void deposit(double amt) {
        balance += amt;
        try {
            Thread.currentThread().sleep();//为了让错误暴露出来
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName()+":"+balance);
    }
}
class Customer extends Thread{
    Account account;
    public Customer(Account account) {
        this.account = account;
    }
    @Override
    public void run() {
        for(int i = ;i < ; i++) {
            account.deposit();
        }
    }
}
public class Test{
    public static void main(String[] args) {
        Account a = new Account();
        Customer c1 = new Customer(a);
        Customer c2 = new Customer(a);
        c1.setName("甲");
        c2.setName("乙");
        c1.start();
        c2.start();
    }
}
 
(8)死锁的问题
不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的资源,就形成了死锁。
代码案例:
public class Test{
    //两个同步资源
    static StringBuffer sb1 = new StringBuffer();
    static StringBuffer sb2 = new StringBuffer();
    public static void main(String[] args) {
        new Thread(){
            public void run(){
                synchronized(sb1){//得到同步锁sb1
                    try{
                        Thread.currentThread().sleep();//通过sleep将问题暴露出来
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    sb1.append("A");
                    synchronized(sb2){//得不到同步锁sb2
                        sb2.append("B");
                        System.out.println(sb1);
                        System.out.println(sb2);
                    }
                }
            }
        }.start();
        new Thread(){
            public void run(){
                synchronized(sb2){//得到同步锁sb2
                    try{
                        Thread.currentThread().sleep();
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                    sb1.append("C");
                    synchronized(sb1){//得不到同步锁sb1
                        sb2.append("D");
                        System.out.println(sb1);
                        System.out.println(sb2);
                    }
                }
            }
        }.start();
    }
}
(9)线程的通信
wait():令当前线程挂起并放弃CPU、同步资源,排队等候再次对资源的访问。
notify() / notifyAll():唤醒一个或所有正在排队等候同步资源的线程,一个是指优先级最高的线程。
java.lang.Object提供的这三个方法只有在synchronized代码块或synchronized方法中才能使用,否则报异常。
代码案例:
使用两个线程交替打印1-100。
class PrintNum implements Runnable{
    int num = ;
    public void run(){
        while(true){
            synchronized(this){
                notify();//唤醒另一个线程,等待p的获得
                if(num <= ){
                    System.out.println(Thread.currentThread().getName()+num);
                    num++;
                }else{
                    break;
                }
                try{
                    wait();//挂起,释放p
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
}
public class Test{
    public static void main(String[] args) {
        PrintNum p = new PrintNum();
        Thread t1 = new Thread(p);
        Thread t2 = new Thread(p);
        t1.setName("甲");
        t2.setName("乙");
        t1.start();
        t2.start();
    }
}
(10)生产者消费者问题
生产者(producer)将产品交给店员(Clerk),由消费者(Customer)从店员取走商品。店员最多只能保存20件商品,如果生产者试图生产更多商品,店员会通知生产者停止生产,店中有空位放产品再通知生产者开始生产;如果店中没有产品,店员通知消费者等一下,有产品再通知消费者取走产品。
class Clerk{
    int product = ;
    public synchronized void addProduct(){
        if(product < ){
            product++;
            System.out.println(Thread.currentThread().getName()+"生产了第"+product+"件产品");
            notifyAll();
        }else{
            try{
                wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
    public synchronized void consumeProduct(){
        if(product > ){
            product--;
            System.out.println(Thread.currentThread().getName()+"消费了第"+product+"件产品");
            notifyAll();
        }else{
            try{
                wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
        }
    }
}
class Producer implements Runnable{
    Clerk clerk = null;
    public Producer(Clerk clerk){
        this.clerk = clerk;
    }
    public void run(){
        System.out.println("生产者开始生产产品");
        while(true){
            try{
                Thread.currentThread().sleep();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            clerk.addProduct();
        }
    }
}
class Customer implements Runnable{
    Clerk clerk = null;
    public Customer(Clerk clerk){
        this.clerk = clerk;
    }
    public void run(){
        System.out.println("消费者开始消费产品");
        while(true){
            try{
                Thread.currentThread().sleep();
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            clerk.consumeProduct();
        }
    }
}
public class Test{
    public static void main(String[] args){
        Clerk c = new Clerk();
        Producer p1 = new Producer(c);
        Customer c1 = new Customer(c);
        Thread t1 = new Thread(p1);
        Thread t2 = new Thread(p1);
        Thread t3 = new Thread(c1);
        t1.setName("生产者1号");
        t2.setName("生产者2号");
        t3.setName("消费者1号");
        t1.start();
        t2.start();
        t3.start();
    }
}
Java语法基础学习DaySeventeen(多线程续)的更多相关文章
- Java语法基础学习DaySixteen(多线程)
		
一.多线程的创建 1.作用 程序需要同时执行两个或多个任务时需要多线程. 程序需要实现需要等待的任务时,如用户输入.文件读写操作.网络操作.搜索等,需要多线程. 需要一些后台运行的程序时,需要多线程. ...
 - Java语法基础学习DayTwenty(反射机制续)
		
一.Java动态代理 1.代理设计模式的原理 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上. 2. ...
 - Java语法基础学习DayFifteen(IO续)
		
一.缓冲流(处理流的一种) 1.作用:可以提高文件操作的效率 2.使用BufferedInputStream和BufferedOutputStream实现非文本文件的复制 特点:flush()方法 代 ...
 - Java语法基础学习DayTen(集合续)
		
一.集合 1.Set:存储的元素是无序的.不可重复的 (1)无序性:无序性不等于随机性,无序指的是元素在底层存储的位置是无序的. (2)不可重复性:当向Set中添加相同的元素时,后添加的元素不能添加进 ...
 - Java语法基础学习DayEighteen(常用类)
		
一.String类 1.特点 String代表不可变的字符序列,底层用char[]存放. String是final的. 2.内存解析 3.常用方法 int length() char charAt(i ...
 - Java语法基础学习DaySeven
		
---恢复内容开始--- 一.包装类——Wrapper 1.定义:针对八种基本数据类型定义相应的引用类型——包装类(封装类) boolean——Boolean byte——Byte ...
 - Java语法基础学习DaySix
		
一.JavaBean——可重用组件 1.JavaBean是指符合以下标准的Java类: (1)类是公共的 (2)有一个无参的公共的构造器 (3)有属性,且有对应的get.set方法 2.好处 用户可以 ...
 - Java语法基础学习DayThree
		
一.流程控制语句补充 1.switch语句 格式: switch(表达式) { case 值1: 语句体1; break; case 值2: 语句体2; break; ... default: 语句体 ...
 - Java语法基础学习DayTwentyOne(网络编程)
		
一.IP地址和端口号 1.作用 通过IP地址,唯一的定位互联网上一台主机. 端口号标识正在计算机上运行的进程,不同进程有不同的端口号,被规定为一个16位的整数0~65535,其中0~1023被预先定义 ...
 
随机推荐
- angular+webpack(二)
			
上篇文章Angular2开发基础之TSC编译 解决如何使用TSC来编译ng2项目,以及如何解决出现的error.这些点是新手容易忽视的内容, 要熟悉ng开发的工具链,还是需要掌握其中的重点.本篇文章是 ...
 - 一个HttpClient使用Windows认证请求WCF服务的例子
			
有个项目需要调用第三方SDK,而SDK获取服务器的已安装的特殊打印机列表返回给调用方. 但我不想依赖这个SDK,因为这个SDK是使用.NET Framework编写的,而我的项目是使用.NET Cor ...
 - 怎样从外网访问内网Rails
			
外网访问内网Rails 本地安装了Rails,只能在局域网内访问,怎样从外网也能访问本地Rails? 本文将介绍具体的实现步骤. 1. 准备工作 1.1 安装并启动Rails 默认安装的Rails端口 ...
 - activiti5/6 系列之--BpmnModel使用
			
BpmnModel对象,是activiti动态部署中很重要的一个对象,如果BpmnModel对象不能深入的理解,那可能如果自己需要开发一套流程设计器,使用bpmn-js使用前端或者C/S展现流程流转而 ...
 - msf下的各种生成payload命令
			
Often one of the most useful (and to the beginner underrated) abilities of Metasploit is the msfpayl ...
 - 【Alpha】Scrum Meeting 5
			
目录 前言 任务分配 燃尽图 会议照片 签入记录 遇到的困难 前言 时间: 4.9日中午11.30 地点:F-220 本次会议旨在统计各位同学的进度以及催促任务的进展. 任务分配 姓名 当前阶段任务 ...
 - Virtual DOM 和 diff 算法
			
virtual DOM : virtual DOM 用 js 模拟 DOM 结构,用 js 来对比前后变化,提高重绘性能. diff: 比如在 git 中,如果用命令 git diff xxx文件 ...
 - Guitar Pro特殊符号讲解之附点音符
			
今天要讲解Guitar Pro里附点音符的作用,附点音符也是大家在编曲做谱的时候,经常需要使用的一个符号,它在Guitar Pro分为附点和双附点. 附点:记在音符符头右边的圆点,用以增长音符的时值. ...
 - 学习了一天的python,终于可以爬爬了-_-
			
恒久恒久以前在语言大陆就听过一种叫,人生苦短,我用python的至理名言.陆陆续续在课下和业余生活中学习的一点python,知道基本的语法和规则,不过py的库实在是太多了,而且许多概念也没有深入的学习 ...
 - luogu2046[NOI2010]海拔 对偶图优化
			
luogu2046[NOI2010]海拔 对偶图优化 链接 https://www.luogu.org/problemnew/show/P2046 思路 海拔一定是0或者1,而且会有一条01交错的分界 ...