一、线程的特点

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(多线程续)的更多相关文章

  1. Java语法基础学习DaySixteen(多线程)

    一.多线程的创建 1.作用 程序需要同时执行两个或多个任务时需要多线程. 程序需要实现需要等待的任务时,如用户输入.文件读写操作.网络操作.搜索等,需要多线程. 需要一些后台运行的程序时,需要多线程. ...

  2. Java语法基础学习DayTwenty(反射机制续)

    一.Java动态代理 1.代理设计模式的原理 使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上. 2. ...

  3. Java语法基础学习DayFifteen(IO续)

    一.缓冲流(处理流的一种) 1.作用:可以提高文件操作的效率 2.使用BufferedInputStream和BufferedOutputStream实现非文本文件的复制 特点:flush()方法 代 ...

  4. Java语法基础学习DayTen(集合续)

    一.集合 1.Set:存储的元素是无序的.不可重复的 (1)无序性:无序性不等于随机性,无序指的是元素在底层存储的位置是无序的. (2)不可重复性:当向Set中添加相同的元素时,后添加的元素不能添加进 ...

  5. Java语法基础学习DayEighteen(常用类)

    一.String类 1.特点 String代表不可变的字符序列,底层用char[]存放. String是final的. 2.内存解析 3.常用方法 int length() char charAt(i ...

  6. Java语法基础学习DaySeven

    ---恢复内容开始--- 一.包装类——Wrapper 1.定义:针对八种基本数据类型定义相应的引用类型——包装类(封装类) boolean——Boolean          byte——Byte ...

  7. Java语法基础学习DaySix

    一.JavaBean——可重用组件 1.JavaBean是指符合以下标准的Java类: (1)类是公共的 (2)有一个无参的公共的构造器 (3)有属性,且有对应的get.set方法 2.好处 用户可以 ...

  8. Java语法基础学习DayThree

    一.流程控制语句补充 1.switch语句 格式: switch(表达式) { case 值1: 语句体1; break; case 值2: 语句体2; break; ... default: 语句体 ...

  9. Java语法基础学习DayTwentyOne(网络编程)

    一.IP地址和端口号 1.作用 通过IP地址,唯一的定位互联网上一台主机. 端口号标识正在计算机上运行的进程,不同进程有不同的端口号,被规定为一个16位的整数0~65535,其中0~1023被预先定义 ...

随机推荐

  1. pascal中的xor,shr,shl,Int(),ArcTan(),copy,delete,pos和leftstr,RightStr等详解

    数学函数:Inc(i)使I:=I+1;Inc(I,b)使I:=I+b;Abs(x)求x的绝对值例:abs(-3)=3Chr(x)求编号x对应的字符. 例:Chr(65)=’A’chr(97)=’a’c ...

  2. EasyUI中使用textbox赋值,setValue和setText顺序问题

    注意两点: 当text和value的值不同时,一定要先赋值Value,然后赋值Text,否则text和value全部为Value的值. 如果只setValue,则使用getText和getValue得 ...

  3. Vue单元测试Karma+Mocha

    Vue单元测试Karma+Mocha Karma是一个基于Node.js的JavaScript测试执行过程管理工具(Test Runner).该工具在Vue中的主要作用是将项目运行在各种主流Web浏览 ...

  4. write RE validation

    正则表达式,又称规则表达式.(英语:Regular Expression,在代码中常简写为regex.regexp或RE),计算机科学的一个概念.正则表达式通常被用来检索.替换那些符合某个模式(规则) ...

  5. Sci_DRead_ParaBuzzerDriver_st_BuzzerSoundOpening1

    extern uint16 Sci_DRead_ParaMotorGroupB_u16_Motor1CinchDoneCurrent1(); * \violation 1503 The functio ...

  6. Python云图——WordCloud了解一下

    字符可以作画(参考前文:使用记事本画出照片) 字符串一样也可以 安装词云WordCloud. pip install wordcloud 编写要生成词云的内容字符串 保存为txt格式就可以了 使用Py ...

  7. docker之Dockerfile实践

    上一篇介绍了Dockerfile中使用的指令,现在开始进行指令实践 先查看下本地的镜像,选一个作为base image: [root@docker ~]# docker images REPOSITO ...

  8. docker启动容器报错: could not synchronise with container process: not a directory

    错误现象 在运行容器时,出现以下错误 [root@localhost test]# docker run -it -d -v $PWD/test.txt:/mydir mytest fd44cdc55 ...

  9. Guitar Pro里自动化编辑器有什么用?如何使用?

    我们在使用操作Guitar Pro来进行吉他学习创作时,会遇到下面几个问题.当我们想要改变全部小节或者某一特定小节的拍速时,就会在想,有没有什么简便工具来实现我们的想法呢?告诉大家,Guitar Pr ...

  10. newcoder Tachibana Kanade Loves Probability(小数点后第k位)题解

    题意:题目链接立华奏在学习初中数学的时候遇到了这样一道大水题: “设箱子内有 n 个球,其中给 m 个球打上标记,设一次摸球摸到每一个球的概率均等,求一次摸球摸到打标记的球的概率” “emmm...语 ...