标签:lan 共享 random ticket 同步 adc false i++ 调用




A:创建线程并启动:继承Thread类
step1:创建子类,继承Thread类。
step2:重写run(),线程体。并发执行的内容,写在这个方法中。
step3:启动线程:start()
/*
class Cat
class Person
class MyException extends Exception{//异常类
}
class MyThread extends Thread{//线程类
}
*/
package com.qf.demo02;
//step1:创建子类,继承Thread类。
//step2:重写run(),线程体。并发执行的内容,写在这个方法中。
//step3:启动线程:start()
//1.子类继承Thread类
class MyThreadA extends Thread{
@Override
public void run() {
//2.此处写要被并发执行的代码。。
for(int i =0;i<100;i++){
System.out.println("\tt1..跳舞ing。。"+i);
}
}
}
class MyThreadB extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("t2..唱歌ing。。"+i);
}
}
}
public class Test1Thread {
public static void main(String[] args) {
//需求:并发实现边唱边跳。。
//创建线程的对象
MyThreadA t1 = new MyThreadA();
MyThreadB t2 = new MyThreadB();
//对象访问方法,直接执行方法中内容。不是并发的范畴
// t1.run();
// t2.run();
// for(int i = 0;i<100;i++){
// System.out.println("\t\tmain...伴舞"+i);
// }
//3.启动线程?表示该线程一切准备就绪,随时可以被cpu执行了。但是cpu是否来执行你,不确定。。
t1.start();//表示启动t1线程
t2.start();//表示启动t2线程
MyThreadA t3 = new MyThreadA();
t3.start();//老子想重来一次。。可否啊?java.lang.IllegalThreadStateException
}
}
B:实现Runnable接口,创建线程的方式二
step1:创建实现类,实现Runnable接口 step2:重写run(),线程体。并发执行的内容,写在这个方法中。 step3:启动线程: 创建实现类的对象:mt 根据实现类对象mt,创建Thread类对象t3,t4 start()启动该线程:t3,t4
对比run()和start() run(),是线程体。就是要并发执行内容。 start(),启动一个线程?就是该线程准备就绪了,随时可以被CPU执行。什么时候执行,CPU自己说了算。 一个线程,只能被start一次。
Thread类的构造方法 new Thread();//并发执行,执行run()方法。 new Thread(Runnable target);//并发执行,执行的run是Runnable接口的实现类实现run方法
package com.qf.demo02;
/*
step1:创建实现类,实现Runnable接口
step2:重写run(),线程体。并发执行的内容,写在这个方法中。
step3:启动线程:
*/
class MyThreadC implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("\t左手画圈。。。"+i);
}
}
}
class MyThreadD implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("右手画方块。。"+i);
}
}
}
public class Test2Runnable {
public static void main(String[] args) {
MyThreadC mt1 = new MyThreadC();
MyThreadD mt2 = new MyThreadD();
Thread t3 = new Thread(mt1);//当启动t3线程,执行mt1对应的run方法
Thread t4 = new Thread(mt2);//当启动t4线程,执行mt2对应的run方法
t3.start();
t4.start();
}
}
Thread类:JDK提供好的类,用于表示一个线程对象。实现类Runnable接口
run(),start()....
Runnable接口:定义了唯一的一个方法:run()——>线程体
方法一:直接继承Thread类
step1:创建一个子类,来继承Thread类 step2:重写run()方法,因为这是线程体:当CPU调度执行该线程的时候,就要执行的是run()方法中的代码。 step3:创建该类的对象,表示一个线程,调用start()进而启动这个线程。意味着该线程一切准备就绪,随时可以被CPU调度执行。但是CPU是否立刻执行?不一定,要看CPU自己。
方法二:实现Runnable接口
step1:创建一个实现类,实现Runnable接口 step2:重写run()方法 step3:先创建该实现类对象:mt,根据实现类对象再创建Thread对象,然后启动。
Thread类的构造方法:
Thread();//创建一个线程对象,执行run()。线程的默认名:Thread-0,1,2... Thread(Runnable target);//创建一个线程对象,指明了target,执行的run是Runnable接口中。 Thread(String name);//创建一个线程,并给起个名字 Thread(Runnable target,String name);
对比两种创建并启动线程的方式:

1、获取当前的线程对象:由Thread类直接调用,获取当前正在被执行的那个线程
static Thread currentThread() ;//返回对当前正在执行的线程对象的引用。
2、线程的名字:当一个线程创建的时候,如果没有设置名称:构造方法设置,或者setName()设置。系统默认的:Thread-0,Thread-1,Thread-2......
String getName()
返回此线程的名称。
void setName(String name)
将此线程的名称更改为等于参数 name 。
3、线程的Id:每个线程创建的时候,由系统自动分配一个Id,long类型的数值,终身不变。从线程的出生到死亡。
该Id值,由系统自动分配,程序员无法手动操作。
long getId()
返回此线程的标识符。
4、线程的优先级:priority
System.out.println("最大优先级:"+Thread.MAX_PRIORITY);//10
System.out.println("最小优先级:"+Thread.MIN_PRIORITY);//1
System.out.println("正常优先级:"+Thread.NORM_PRIORITY);//5
当一个线程被创建的时候,由系统自动分配一个优先级,固定都是正常优先级:5
但是程序员可以根据需求,手动调整线程的优先级。
int getPriority()
返回此线程的优先级
void setPriority(int newPriority)
更改此线程的优先级。
有个坑:容易误解为:优先级别高的先执行,然后再执行优先级低的。大大的错误XXXXX
优先级别高,被CPU调度执行的机会就多。但是不绝对。
优先级别低,被CPU执行的机会就少,但是也不绝对。
5、线程的睡眠:重要的方法
static void sleep(long millis)
使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。
静态方法,应该由类直接调用,对象也可以调用,有坑:不是谁调用就谁睡,而是当前正在执行的线程进入睡眠了。和谁调用无关。
阻塞:
6、线程合并
void join()
等待这个线程死亡。
t1线程,t2线程,main线程
t1,t2,main--->3条线程抢占资源
某一个时刻:main线程中:t1.join(),主线程要等待t1线程死亡之后再执行
t1,t2--->2条线程抢占资源,main等
t1结束后,main线程再执行
阻塞:
Scanner scan = new Scanner(System.in);
scan.nextInt();//阻塞式。等--->解除阻塞,读取到一个键盘输人
7、守护线程
setDaemon();
为前台线程服务,如果所有的前台线程都结束了,那么守护线程也就结束了。
GC:垃圾自动回收机制。JVM启动后,创建主线程执行main()的时候。。。随之而创建并启动的还有很多后台线程,比如gc()
8、其他方法:
package com.qf.demo02;
class MyThreadF extends Thread{
@Override
public void run() {
for(int i=0;i<100;i++){
System.out.println("\t"+Thread.currentThread().getName()+"\t"+i);
}
}
}
public class Test7Join {
public static void main(String[] args) {
MyThreadF t1 = new MyThreadF();
MyThreadF t2 = new MyThreadF();
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
for(int i =0;i<100;i++){
System.out.println("main..."+i);
if(i == 50){
try {
/*
* 线程合并
void join()
等待这个线程死亡。
主线程中:t1.join(),主线程如果持有资源,会将机会让给t1线程。
t1线程在main线程之前执行。t1结束后,main线程才会执行。
*/
t1.join();//?
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
package com.qf.demo02;
class MyThreadG extends Thread{
private int i=0;
public void run() {
while(true){
System.out.println(Thread.currentThread().getName()+"\t"+i);
i++;
}
}
}
public class Test8Daemon {
public static void main(String[] args) {
MyThreadG t1 = new MyThreadG();
//将t1线程设置为守护线程
t1.setDaemon(true);
t1.start();
for(int i=0;i<1000;i++){
System.out.println("\tmain.."+i);
}
}
}
线程的生命周期:5种,6种,7种。
线程的一生,就好比秀女的一声。
线程new出来:新建
准备就绪,启动:start:就绪状态
如果被CPU调度执行:运行状态,run()方法
出生-->就绪-->运行-->死亡


多个线程访问共享的数据,临界资源。
多个线程之间存在共享的数据。一条线程执行过程中,其他线程也可以访问,可能会修改数据的值。造成的共享数据的不安全。叫做临界资源的安全问题。
package com.qf.demo03;
class MyThread1 implements Runnable{
private int tickets = 10;
private Object obj = new Object();//成员变量,属于对象
@Override
public void run() {
while(true){
/*
* 需要使用synchronized同步代码块,防止共享数据存在不安全性。
*
* 锁定的对象:谁作为锁头?
* this:mt
* obj:
* "abc":所谓的大招,字面常量,内存中独一份。
*/
//t1,t2,t3,t4
synchronized (this) {//任意对象,只要是这4个线程共同的对象即可。 t1给obj上锁
if(tickets > 0){
try {
Thread.sleep((int)(Math.random()*101));//t1睡去了。。释放cpu资源
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",出售:"+tickets);
tickets--;
}else{
System.out.println(Thread.currentThread().getName()+",售票结束。。");
break;
}
}
}
}
}
public class Test9_SaleTicket {
public static void main(String[] args) {
/*
* 2.模拟火车站卖票:使用4个线程,模拟4个售票口,出售共同的100张票。
分别使用两种创建线程的方式实现。
思路:
四个售票口:4条线程:t1,t2,t3,t4
出售100张票:int ticket:100,99,98,97.。。。。1
*/
MyThread1 mt = new MyThread1();
Thread t1 = new Thread(mt, "售票口1");
Thread t2 = new Thread(mt, "售票口2");
Thread t3 = new Thread(mt, "售票口3");
Thread t4 = new Thread(mt, "售票口4");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步的方式一:同步代码块
synchronized(锁对象){//上锁
//被同步的代码,每次只能被一个线程执行,中间不能被其他线程插入执行
}//锁打开
注意点:
同步的原理:锁定一个对象。(对象可以和程序无关,但必须是多个线程访问的共同的对象才可以)。
常用的锁对象:
this,创建一个对象,传入进去。
大招:类名.class,字符串常量:"abc"
package com.qf.demo03;
class MyThread2 extends Thread{
private static int ticket = 10;
// private static Object obj = new Object();//成员变量
private Object obj;
public MyThread2(String name,Object obj) {
super(name);
this.obj = obj;
}
@Override
public void run() {
while(true){
/*
* 如果是继承Thread类的方式,不能用this作为锁对象,因为创建多个子线程对象,this其实指代的是多个对象,不是同一个,锁不住
*
* 大招:
* 字符串常量:"abc"
* 类名.class
*
* java程序:源代码-->class
* 进行编译:class字节码文件
* 万事万物皆对象:字节码文件---->类和它对应的:Class
*
*
*
* Class类
* 对象.getClass()-->Class
* 类名.class-->Class
* Class.forName("包名.类名")-->Class
*/
synchronized (MyThread2.class) {
if(ticket > 0){
try {
Thread.sleep((int)(Math.random()*100));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+",出售:\t"+ticket);
ticket--;
}else{
System.out.println(Thread.currentThread().getName()+",售票结束。。");
break;
}
}
}
}
}
public class Test10_SaleTicket {
public static void main(String[] args) {
Object obj = new Object();//作为锁对象
MyThread2 t1 = new MyThread2("售票口1",obj);
MyThread2 t2 = new MyThread2("售票口2",obj);
MyThread2 t3 = new MyThread2("售票口3",obj);
MyThread2 t4 = new MyThread2("售票口4",obj);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
同步的方式二:同步方法
//该方法,每次只能有一个线程来执行,期间,不能被其他的线程插入执行。
public synchronized void 方法名(){
}
同步的优缺点:
解决了多线程之间的共享数据的安全问题
降低效率,容易死锁。
死锁:多个线程互相持有对象,僵持的现象。
解决死锁:
1、减少成员变量的使用。
2、加大锁的粒度。不要锁小对象,锁大对象。
package com.qf.demo03;
class MyThread3 implements Runnable{
private static String bread = "面包";
private static String milk = "牛奶";
boolean flag ;//true, false
@Override
public void run() {
if(flag == true){//t1
synchronized (bread) {
System.out.println(Thread.currentThread().getName()+"\t,已经拥有了"+bread+",还想要。。"+milk);
synchronized (milk) {
System.out.println(Thread.currentThread().getName()+",两者都拥有了。。");
}
}
}else{//t2
synchronized (milk) {
System.out.println(Thread.currentThread().getName()+"\t,已经拥有了"+milk+",还想要。。"+bread);
synchronized (bread) {
System.out.println(Thread.currentThread().getName()+",两者都拥有了。。");
}
}
}
}
}
public class Test11_DeadLock {
public static void main(String[] args) {
/*
* t1:张三
* t2:李四
* t1锁定break,要milk
* t2锁定milk,要bread
*/
MyThread3 mt1 = new MyThread3();
MyThread3 mt2 = new MyThread3();
mt1.flag = true;
mt2.flag = false;
Thread t1 = new Thread(mt1,"张三");
Thread t2 = new Thread(mt2,"李四");
t1.start();
t2.start();
}
}

标签:lan 共享 random ticket 同步 adc false i++ 调用
原文地址:https://www.cnblogs.com/yzg-14/p/13401441.html