——- android培训、java培训、期待与您交流! ———-
线程和进程是什么?:
进程:是一个正在执行中的程序。每一个进程执行都有一个执行顺序。
该顺序是一个执行路径,或者叫一个控制单元。
线程:就是进程中的一个独立的控制单元。
线程在控制着进程的执行。
一个进程中至少有一个线程。
java虚拟机启动的时候会有一个进程java.exe。
该进程中至少有一个线程负责java程序执行。
而且这个线程运行的代码存在于main方法中。
该线程称之为 主线程。
创建线程的第一种方式:继承Thread类
步骤:
1,定义类继承Thread。
2,复写Thread类中的run方法。
目的:将自定义代码存储在run方法。让线程运行。
3,调用线程的start方法。
该方法有两个作用:启动线程、调用run方法
代码示例:
class ThreadDemo extends Thread {//1,继承Thread类
public void run() {//2,复写run方法
for (int i = 0; i < 60; i++) {
System.out.println("Thread----" + i);
}
}
}
public class Demo {
public static void main(String[] args) {
ThreadDemo td = new ThreadDemo();// 创建好一个线程
td.start(); // 3,开启线程并执行该线程的run方法
// td.run //仅仅是普通的对象调用方法。线程虽然创建了,但是并没有运行
for (int i = 0; i < 60; i++)
System.out.println("hello------" + i);
}
}
运行此程序发现:运行结果每一次都不同。
因为多个线程都获取cpu的执行权。cpu执行到谁,谁就运行。
明确一点,在某一个时刻,只能有一个程序在运行。(多核除外)
cpu在做着快速的切换,以达到看上去是同时运行的效果。
我们可以形象把,多线程的运行行为理解为互相抢夺cpu的执行权。
这就是多线程的一个特性:随机性。谁抢到谁执行,至于执行多长时间,cpu说的算
td.start(); 和 td.run();的区别:
td.start();开启线程并执行该线程的run方法,相当于程序中有两个线程
一个线程执行main中的方法,一个线程执行run中的方法
td.run();就是普通的对象调用,并没有启动线程,相当于程序中只有一个
主线程在执行,当执行到td.dun()时,就调用该对象的方法,执行完后再回到
main方法中继续执行
注意:如果主函数创建两个线程在执行,那么栈中会创建两个run方法。
两个run方法中的内容都是独立的。
如:两个run方法中都有变量 num ,那么num在两个run方法中互不干扰。
/*
* 练习:
* 创建两个线程,和主线程交替运行。
*
* 线程都有自己默认的名称。
* 名称格式为Thread-编号, 该编号从0开始
*
* Thread.currentThread(); 获取当前线程对象。
* getName(); 获取线程名称
*
* 设置线程名称:setName或者调用线程的构造函数
*/
class ThreadDemo extends Thread{
private String name;
public ThreadDemo(String name){
super(name);//调用Thread的构造函数,设置线程名称
}
public void run(){
for(int i=0;i<60;i++){
//this.getName获取线程名称,默认名称格式为 Thread-编号
//this.getName可以改成Thread.currentThread().getName,因为Thread.currentThread()
//可以获取线程的对象
System.out.println(this.getName() + "..." + i);
}
}
}
public class Demo {
public static void main(String[] args) {
ThreadDemo td1 = new ThreadDemo("td1...哈哈");
ThreadDemo td2 = new ThreadDemo("td2...呵呵");
td1.start();
td2.start();
//td1.run(); //这种方式并不是创建两个线程并执行,只相当于方法调用
//td2.run(); //这种方式并不是创建两个线程并执行,只相当于方法调用
for(int i = 0;i<60;i++)
System.out.println("main....嘿嘿");
}
}
创建线程的第二种方式:实现Runnable接口
步骤:
1.定义类实现Runnable接口
2.覆盖Runnable接口中的run方法,将要运行的代码存放在run方法中
3.通过Thread类建立线程对象
4.将Runnable接口的子类对象,作为实际参数传递给Thread类的构造函数。
5.调用Thread类的start方法开启线程,使得start方法自动调用Runnable子类的的run方法。
实现方式(implements Runnable)和继承方式(extends Thread)有什么区别呢?(面试题)
继承Thread:线程代码存放在Thread的子类的run方法中。但是具备单继承的局限性
实现Runnable:线程代码存放在实现Runnable接口的子类的run方法中。避免了单集成的局限性
代码示例:
/*
需求:简单的卖票程序。
多个窗口同时卖票(假设为4个卖票窗口)
*/
class Ticket implements Runnable { // 创建一个 票 类,实现了Runnable的run方法。注意:不是继承Thread
private int i = 100;
public void run() {//将要运行的代码存放在run方法中
while (true) {
if (i > 0)
System.out.println(Thread.currentThread().getName() + "..."
+ i--);
else
return;
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket t = new Ticket();
// 通过Thread类建立线程对象,并将Runnable接口的子类对象,
//作为实际参数传递给Thread类的构造函数
Thread th1 = new Thread(t);
Thread th2 = new Thread(t);
Thread th3 = new Thread(t);
Thread th4 = new Thread(t);
th1.start();//调用Thread类的start方法开启线程
th2.start();
th3.start();
th4.start();
}
}
同步代码块:synchronized
synchronized(对象)
{
需要被同步的代码块
}
同步代码快中的 对象 如同锁。持有锁的线程可以在同步中执行。
没有持有锁的线程即使获取cpu的执行权,也进不去,因为没有获取锁。(如同火车上的卫生间,当有人进去锁门后,外边的人只能等待)
同步的前提:(注意:如果加锁过后程序还是不安全,应该考虑程序中的锁是否符合这两个前提)
1,必须要有两个或者两个以上的线程。
2,必须是多个线程使用同一个锁。
必须保证同步中只能有一个线程在运行。
好处:解决了多线程的安全问题。
弊端:多个线程需要判断锁,比较消耗资源。
代码示例:
/*
通过运行结果:发现出现了0,-1,-2等错票
多线程的运行出现了安全问题
问题的原因:
当多条语句在操作同一个线程的共享数据时,一个线程对多条语句只执行了一部分,
还没有执行完,另一个线程参与进来执行。导致共享数据的错误。
解决办法:
对多条操作共享数据的语句,只能让一个线程执行完。在执行过程中,
其他线程不可以参与执行。
java对于多线程的安全问题提供了专业的解决方式。
就是同步代码块:
*/
class Ticket implements Runnable { // 创建一个 票 类,实现了Runnable的run方法
private int i = 100;
public void run() {
while (true) {
if (i > 0) {
try {
Thread.sleep(10);// 使线程执行10后,等待10毫秒再继续执行
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "..."+ i--);
} else {
return;
}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread th1 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
Thread th2 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
Thread th3 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
Thread th4 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
th1.start();
th2.start();
th3.start();
th4.start();
}
}
由于出现了错票,将以上代码修改为以下代码
class Ticket implements Runnable { // 创建一个 票 类,实现了Runnable的run方法
private int i = 100;
Object obj = new Object();
public void run() {
while (true) {
synchronized (obj) {//加上这句语句后,if代码没有执行完,其他的线程不许进入if语句
if (i > 0) {
try {
Thread.sleep(10);// 使线程执行10后,等待10毫秒再继续执行
} catch (InterruptedException e) {
}
System.out.println(Thread.currentThread().getName() + "..."
+ i--);
} else {
return;
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
Ticket t = new Ticket();
Thread th1 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
Thread th2 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
Thread th3 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
Thread th4 = new Thread(t);// 创建了一个线程,相当于一个售票窗口
th1.start();
th2.start();
th3.start();
th4.start();
}
}
/*
需求:
银行有一个金库。
有两个用户分别存300元,每次存100元,存3次。
目的:该线程是否有安全问题,如果有,如何解决
如何找到问题:
1,明确哪些代码是多线程运行代码。
在本题中,多线程运行的代码是run方法和add方法
2,明确哪些是共享数据
在本题中,对象j是共享数据,成员函数num也是共享数据。
3,明确多线程运行代码中,哪些语句是操作共享数据的
在本题中,j.add是操作共享数据的,但是j.add只有一句话,不能被多线程给分开读。
而num有两句话(num+=x,System.out.println(num)),是可以被多线程分开读的,
如:第一个线程读到num+=x时,第二个线程插进来读取nun+=x,而导致第一个线程没有读到System.out.println(num)。
所以,本题中要分析的就是num+=x,System.out.println(num)。
*/
class JinKu { //金库类
private int num = 0;
Object obj = new Object();
public void add(int x) {
num += x;
// try {Thread.sleep(10);} catch (InterruptedException e) {}//此处代码可以不用try,可以把异常抛出去。但是这样做在run方法中,必须处理异常
System.out.println(num);
}
// 该线程有问题,如果在该线程add方法中的num+=x下边,
// 加入try {Thread.sleep(10);} catch (InterruptedException e) {}
// 那么就会出现错误,应该将add()方法改为如下
// public void add(int x){
// synchronized (obj) {
// num+=x;
// try {Thread.sleep(10);} catch (InterruptedException e) {}
// System.out.println(num);
// }
// }
}
class YongHu implements Runnable { //用户类
JinKu j = new JinKu();
public void run() {
for (int i = 0; i < 3; i++)
j.add(100);
}
}
public class Demo {
public static void main(String[] args) {
YongHu yh = new YongHu();
Thread th1 = new Thread(yh);
Thread th2 = new Thread(yh);
th1.start();
th2.start();
}
}
同步函数和静态同步函数:
同步函数:public synchronized void show(){}
静态同步函数:public static synchronized void show(){}
同步函数用的是哪一个锁呢?
函数需要被对象调用。那么函数都有一个所属对象引用。就是this。
所以同步函数用的锁是this。
(记住结论就行:同步函数用的锁是this)
如果同步函数被静态修饰后,使用的锁是什么呢?
通过验证,发现不再是this,因为静态方法中也不可以定义this。
静态进内存时,内存中没有本类对象,但是一定有该类对应的字节码对象。
类名.class 该对象的类型是class
静态的同步方法,使用的锁是该方法所在类的字节码文件对象。类名.class
(记住结论就行:静态同步函数用的锁是 类名.class)
代码示例:
/*
卖票的例子,使用同步函数来加锁
*/
class Test implements Runnable {
int count = 100;
public void run() {
while (true) {
maiPiao();
}
}
public synchronized void maiPiao() {//在函数上加同步
if (count > 0) {
try {
Thread.sleep(10);
} catch (Exception e) {
}
System.out.println(Thread.currentThread().getName() + "..."
+ count--);
}
}
}
public class Demo {
public static void main(String[] args) {
Test t = new Test();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
new Thread(t).start();
}
}
死锁:
死锁就是,在我这个锁中用你那个锁,在你那个锁中又要用到我这个锁。
形象的比喻就是:两根筷子。A一根B一根,每人只拿一根筷子就无法吃饭。
除非B把筷子给A,或者A把筷子给B,这样才能吃饭。如果A不给B,B也不给A,
那么就无法吃饭,导致死锁
/*
死锁程序(面试喜欢考)
*/
class Test1 implements Runnable {
public void run() {
while (true) {
//使用Test1.class锁
synchronized (Test1.class) {
System.out.println("Test1...Test1.class");
//使用Test2.class锁
synchronized (Test2.class) {
System.out.println("Test1...Test2.class");
}
}
}
}
}
class Test2 implements Runnable {
public void run() {
while (true) {
//使用Test2.class锁
synchronized (Test2.class) {
System.out.println("Test2...Test2.class");
//使用Test1.class锁
synchronized (Test1.class) {
System.out.println("Test2...Test1.class");
}
}
}
}
}
public class Demo {
public static void main(String[] args) {
new Thread(new Test1()).start();
new Thread(new Test2()).start();
/*
本示例中,发生死锁的原因:
当Test1类中的程序输出"Test1...Test1.class"时,
cpu突然切换到Test2类中的线程。
于是就输出了"Test2...Test2.class"。
这时,Test2类中的程序继续执行,读到了Test1.class锁,
由于Test1.class锁在Test1类中没有释放,
于是程序就在Test2类中等待Test1.class锁释放。
这时,cpu切换到了Test1类中的程序,
由于Test2.calss锁又在Test2类中序没有释放,
于是Test1类中的程序在等待Test2.class释放。
最终导致了,Test1类在等待Test2类释放锁,Test2类在等待Test1类释放锁,
从而导致了死锁
*/
}
}
多线程中的懒汉式
代码示例:
class Single{
private static Single s = null;
private Single(){}
public static Single getSingle(){
if (s == null) {//双重if判断语句
synchronized (Single.class) {
if (s == null) {
s = new Single();
}
}
}
return s;
}
}
线程间的通讯:
线程间的通讯:可以理解为,一个线程在存数据,另一个线程在取数据。
如:
拉煤的例子:一个车在送煤,另外一个车在取煤。
生产者例子:生产者不断的生产物品,而消费者不断的消费物品
代码示例:
class Person {// 定义一个人类
String name;
String sex;// 性别
}
class Input implements Runnable {
Person p;
int x = 0;// 用来控制if和else语句,x为0时执行if,x为1时执行else
public Input(Person p) {// 为了保证和Output类中的Person是同一个对象,将参数定义为Person,
// 这样就能确保传递进来的参数和Outpue类一样
this.p = p;
}
public void run() {//此run方法不断的初始化姓名和性别。
while (true) {
synchronized (p) {// 此处的锁用p是因为,本类中的p和Output中的p是同一个对象,如果
// 此处不加同步输出结果不是我们想要的//
//可能会输出 “张三,nv”,“lili,男”等错误
if (x == 0) {
p.name = "张三";
p.sex = "男";
} else {
p.name = "lili";
p.sex = "nv";
}
x = (x + 1) % 2;
}
}
}
}
class Output implements Runnable {
Person p;
public Output(Person p) {// 为了保证和Input类中的Person是同一个对象,将参数定义为Person,
// 这样就能确保传递进来的参数和Input类一样
this.p = p;
}
public void run() {//此run方法不断的获取姓名和性别
while (true) {
synchronized (p) {// 此处的锁用p是因为,本类中的p和Input中的p是同一个对象
// 此处不加同步输出结果会报错
System.out.println(p.name + "...." + p.sex);
}
}
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Person();
Input in = new Input(p);
Output out = new Output(p);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
发现,每次输出时候并不是存放一个数据输出一次,而是一下子输出一大片,
于是,将代码修改为下面的代码,让程序每存放一个数据,就输出一个数据。
使用等待唤醒机制。既:wait() 和 notify()
注意:wait()、notify()方法必须使用在同步代码块或者同步方法中
class Person {// 定义一个人类
String name;
String sex;// 性别
boolean flag = false; //标记是否已经存放了name和sex
}
class Input implements Runnable {
Person p;
int x = 0;// 用来控制if和else语句,x为0时执行if,x为1时执行else
public Input(Person p) {// 为了保证和Output类中的Person是同一个对象,将参数定义为Person,
// 这样就能确保传递进来的参数和Outpue类一样
this.p = p;
}
public void run() {
while (true) {
synchronized (p) {// 此处的锁用p是因为,本类中的p和Output中的p是同一个对象,如果
// 此处不加同步输出结果会报错
if(p.flag)//如果存放了name和sex,那么就进入等待,当进入等待后,后边的语句都不执行
try {p.wait();} catch (InterruptedException e) {}//使用wait()时,必须标识对象(锁)
if (x == 0) {
p.name = "张三";
p.sex = "男";
} else {
p.name = "lili";
p.sex = "nv";
}
p.flag = true; //存放name和sex后,让标记为true
p.notify(); //在进入等待之前先唤醒Output中的wait(),使用notify()时,必须标识对象(锁)
x = (x + 1) % 2;
}
}
}
}
class Output implements Runnable {
Person p;
public Output(Person p) {// 为了保证和Input类中的Person是同一个对象,将参数定义为Person,
// 这样就能确保传递进来的参数和Input类一样
this.p = p;
}
public void run() {
while (true) {
synchronized (p) {// 此处的锁用p是因为,本类中的p和Input中的p是同一个对象
// 此处不加同步输出结果会报错
if(!p.flag) //如果已经输出了name和sex,那么就进入等待,进入等待后,后边的语句都不执行
try {p.wait();} catch (InterruptedException e) {}//使用wait()时,必须标识对象(锁)
System.out.println(p.name + "...." + p.sex);
p.flag = false; //输出name和sex后,让标记为false
p.notify(); 在进入等待之前先唤醒Input中的wait(),使用notify()时,必须标识对象(锁)
}
}
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Person();
Input in = new Input(p);
Output out = new Output(p);
Thread t1 = new Thread(in);
Thread t2 = new Thread(out);
t1.start();
t2.start();
}
}
/*
* 练习:生产者和消费者
* 要求:定义4个线程,生产者有两个,消费者有两个,当生产一个,必须被消费一个
*
*
* 对于多个生产者和消费者。
* 为什么要定义while判断标记。
* 原因:让唤醒线程再判断一次标记
*
* 为什么定义notifyAll。
* 因为需要唤醒对方线程。
* 因为只用notify容易出现只唤醒本方线程的情况。导致程序中所有线程都等待
*
* 但是呢notifyAll不仅唤醒了对方,而且还唤醒了本方
*/
class Test{
int i = 0;
boolean flag = false;
// t1 t2
public synchronized void shengChan(String name){
while(flag)//必须用while循环,否则运行结果不是想要的
try {wait();} catch (InterruptedException e) {} // 【t1(放弃执行资格) t2(放弃执行资格,同时唤醒t1) t1(由于循环判断,放弃执行资格)】
System.out.println(Thread.currentThread().getName()+"..."+name+"..."+i);
flag = true;
notifyAll();//必须全部唤醒,否则会出现所有线程都等待
}
// t3 t4
public synchronized void xiaoFei(String name){
while(!flag)//必须用while循环,否则运行结果不是想要的
try {wait();} catch (InterruptedException e) {}// 【t3(放弃执行资格,同时唤醒t1,t2) t4(放弃执行资格,同时唤醒t3) t3(由于循环判断,放弃执行资格)】
System.out.println(Thread.currentThread().getName()+"..."+name+"......."+i++);
flag = false;
notifyAll();//必须全部唤醒,否则会出现所有线程都等待
}
}
class ShengChan implements Runnable{
Test t;
public ShengChan(Test t){
this.t = t;
}
public void run(){//此run方法不断的生产数据
while(true){
t.shengChan("生产者");
}
}
}
class XiaoFei implements Runnable{
Test t;
public XiaoFei(Test t){
this.t = t;
}
public void run(){//此run方法不断的获取数据
while(true){
t.xiaoFei("消费者");
}
}
}
public class Demo {
public static void main(String[] args) {
Test t = new Test();
ShengChan sc = new ShengChan(t);
XiaoFei xf = new XiaoFei(t);
Thread t1 = new Thread(sc);
Thread t2 = new Thread(sc);
Thread t3 = new Thread(xf);
Thread t4 = new Thread(xf);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
JDK 1.5中提供了多线程升级解决方案。Lock
将同步synchronized替换成了Lock操作。
将Object中的wait,notify,notifyAll,替换成了Condition对象。
该对象可以Lock锁进行获取。
该示例中,实现了本方只唤醒对方操作。而上个示例的notifyAll不仅唤醒了对方,还唤醒了本方
代码示例:
import java.util.concurrent.locks.*;
class Test {
int i = 0;
boolean flag = false;
private Lock lock = new ReentrantLock();// 创建一个锁,替代了synchronized
private Condition condition_shengchan = lock.newCondition(); // 创建一个条件,替代了wait(),notify,notifyAll
private Condition condition_xiaofei = lock.newCondition(); // 创建一个条件,替代了wait(),notify,notifyAll
public void shengChan(String name) throws InterruptedException {//生产者
lock.lock();// 相当于synchronized,只不过是替换了synchronized
try {
while (flag)// 必须用while循环,否则运行结果不是想要的
condition_shengchan.await();// 使生产者进入等待状态。使用该方法会抛出异常,所以在方法上声明异常,
//该方法使生产这个锁进入等待
System.out.println(Thread.currentThread().getName() + "..." + name
+ "..." + i);
flag = true;
condition_xiaofei.signal();//在生产者进入等待之前,先唤醒消费,使得消费者开始执行
} finally {
lock.unlock();// 锁必须要被释放,必须要执行,所以放在finally中,就算出现异常,此处依旧执行
}
}
public void xiaoFei(String name) throws InterruptedException {//消费者
lock.lock();// 相当于synchronized,只不过是替换了synchronized
try {
while (!flag)// 必须用while循环,否则运行结果不是想要的
condition_xiaofei.await();// 使消费者进入等待状态。使用该方法会抛出异常,所以在方法上声明异常。
////该方法使消费这个锁进入等待
System.out.println(Thread.currentThread().getName() + "..." + name
+ "......." + i++);
flag = false;
condition_shengchan.signal();//在消费者进入等待之前,先唤醒生产者,使得生产者开始执行
} finally {
lock.unlock();//锁必须要被释放,必须要执行,所以放在finally中,就算出现异常,此处依旧执行
}
}
}
class ShengChan implements Runnable {
Test t;
public ShengChan(Test t) {
this.t = t;
}
public void run() {
while (true) {
try {
t.shengChan("生产者");//由于该方法抛出异常,所以这里需要捕获异常
} catch (InterruptedException e) {
}
}
}
}
class XiaoFei implements Runnable {
Test t;
public XiaoFei(Test t) {
this.t = t;
}
public void run() {
while (true) {
try {
t.xiaoFei("消费者");//由于该方法抛出异常,所以这里需要捕获异常
} catch (InterruptedException e) {
}
}
}
}
public class Demo {
public static void main(String[] args) {
Test t = new Test();
ShengChan sc = new ShengChan(t);
XiaoFei xf = new XiaoFei(t);
Thread t1 = new Thread(sc);
Thread t2 = new Thread(sc);
Thread t3 = new Thread(xf);
Thread t4 = new Thread(xf);
t1.start();
t2.start();
t3.start();
t4.start();
}
}
interrupt
stop方法已经过时,那么如何停止线程?
只有一种方式,就是让run方法结束。
开启多线程运行,运行代码通常是循环结构。
只要控制住循环,就可以让run方法结束,也就是线程结束。
特殊情况:
当线程处于冻结状态。
就不会读取到标记。那么线程就不会结束。
当没有指定的方式让冻结的线程恢复到运行状态时,这时需要对冻结进行清除。
强制让线程恢复到运行状态中来。这样就可以操作标记让线程结束。
Thread类提供该方法 interrupt();
import java.util.Scanner;
class Test implements Runnable{
boolean flag = true;
public synchronized void run(){
while(flag){
try {
wait();//使用interrupt()方法,强制让线程抛出异常。在异常中将循环标记置为false,当重新回到循环判断时,由于flag为false,循环就结束了
} catch (InterruptedException e) {
flag = false;
}
}
}
}
public class Demo {
public static void main(String[] args) {
Thread t = new Thread(new Test());
t.start();
Scanner input = new Scanner(System.in);
input.nextLine();//输入一个字符串
t.interrupt();//当输入一个字符串后,直接中断t线程
}
}
setDaemon(boolean)
将线程标记为守护线程 setDaemon(boolean);
守护线程必须在线程开启前使用。既:在t.start()方法前使用
守护线程代表的是,当主线程结束时,守护线程也结束。它随着主线程存在而存在,随着主线程消失而消失
class Test implements Runnable{
public void run(){
while(true){
System.out.println(Thread.currentThread().getName());
}
}
}
public class Demo {
public static void main(String[] args) {
Thread t1 = new Thread(new Test());
Thread t2 = new Thread(new Test());
//标记为守护线程。在t.start()方法前使用
t1.setDaemon(true);//true代表开启守护线程。
t2.setDaemon(true);
t1.start();
t2.start();
for(int i=0;i<50;i++){
System.out.println("main...");
}
}
}
join
join:
当A线程执行到了 B线程的.join方法时 ,A就会等待。等B线程执行完,A才会执行。
join可以用来临时加入线程执行
class ThreadDemo implements Runnable{
public void run(){
for(int i=0;i<30;i++){
System.out.println(Thread.currentThread().getName()+"..."+i);
}
}
}
public class Demo {
public static void main(String[] args) {
ThreadDemo st = new ThreadDemo();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
try {
t1.join();//当主线程执行到此处时候,先等待t1线程执行完毕,主线程才继续往下执行
} catch (InterruptedException e) {
}
t2.start();
for(int i=0;i<30;i++){
System.out.println("main........."+i);
}
System.out.println("over");
}
}
setPriority
设置线程的优先级 setPriority(int num);
输出线程的主要信息 toString();
class ThreadDemo implements Runnable{
public void run(){
for(int i=0;i<30;i++){
System.out.println(Thread.currentThread().toString());
/*
结果:
Thread[Thread-0,10,main]
Thread[Thread-1,5,main]
Thread-0 或者Thread-1代表线程的编号。
main代表该线程是被哪个线程开启的。很明显,此线程是被主线程开启的
5代表优先级。优先级越高,获取到cpu执行权的概率越高。优先级最高为10
*/
}
}
}
public class Demo {
public static void main(String[] args) {
ThreadDemo st = new ThreadDemo();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();//主线程开启了一个线程
t1.setPriority(10);//设置t1线程的优先级为10。那么t1线程抢到cpu执行权的概率就比较高
t2.start();//主线程开启了一个线程
}
}
Thread.yield()
释放执行权Thread.yield();
当线程执行到Thread.yield()时,线程会放弃执行权(既:cpu切换到该线程时,该线程不执行),然后再重新获取执行权。
例如:
当线程A放弃执行权后,线程B就更容易抢到cpu的执行权。
这样使得程序比较协调,使得每个线程被执行到的概率大些。既:A执行一次,B执行一次,A执行一次…..
class ThreadDemo implements Runnable{
public void run(){
for(int i=0;i<70;i++){
System.out.println(Thread.currentThread().toString());
Thread.yield();
}
}
}
public class Demo {
public static void main(String[] args) {
ThreadDemo st = new ThreadDemo();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
t1.start();
t2.start();
}
}