线程和进程的区别:
https://blog.csdn.net/liutao43/article/details/114673657
Java实现线程的方法
Java支持多线程,并且实现也比较容易,并且Java将线程抽象为了类,并且在jvm中,一个线程代表一个独立的栈空间。
继承Thread方法,实现多线程
这一种方法主要是注意区别start和run方法的区别
public class Main {
public static void main(String[] args){
MyThread myThread = new MyThread();
/*
* 启动线程
* start()方法作用:启动一个分支线程,在jvm中开辟一个新的栈空间,代码任务完成,瞬间就结束了,线程就启动了
* start()方法执行成功之后,会自动调用run()方法,并且在分支栈底部压栈
* run方法在分支栈的栈底部,main方法在主线程栈底部.run()和main()是平级的
* run()方法不会启动线程,不能并发执行
* start()会重新开辟出一块内存空间
*/
myThread.start();
for(int i = 0 ; i < 1000 ; i++) {
System.out.println("我是main线程" + i);
}
}
}
/*
* 实现线程直接编写一个类继承java.lang.Thread,必须重写run方法
*/
class MyThread extends Thread{
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i++) {
System.out.println("我是Thread线程" +i);
}
}
}
实现Runnable接口中的run方法
实现接口Runnable
import java.util.Scanner;
/*
* 实现线程:编写一个类,用Runnable接口,实现run()方法,然后封装到Thread中
*/
public class Main {
public static void main(String[] args){
// MyRunable myr = new MyRunable();
// Thread myThread = new Thread(myr);
/*
* 启动线程,开辟一个新分支栈空间
* 会自动调用实现类中的run方法
*/
// myThread.start();
/*
* 采用匿名内部类实现线程
*/
Thread myThread = new Thread(new Runnable() {
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i++) {
if(i == 500) {
Scanner s= new Scanner(System.in);
String string = s.nextLine();
System.out.println(string);
}
System.out.println("分支线程 --->" + i);
}
}
});
myThread.start();
for(int i = 0 ; i < 1000 ; i++) {
System.out.println("主线程--->" + i);
}
}
}
/*
* 实现Runnable接口,实现run()方法
* 建议使用使用接口这种方式,面向抽象编程,并且可以继承其它的类
*/
class MyRunable implements Runnable{
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i++) {
System.out.println("分支线程 --->" + i);
}
}
}
线程状态
线程和进程的状态是类似的:
设置和获取当前线程的名称
获取当前线程:默认的分支线程名称是Thread-0,Thread-1,Thread-2…
/*
* 获取线程名字: 线程对象:getName()
* 获取线程对象:static void currentThread()
* 修改线程对象的名字: 线程对象.setName()
*/
public class Main {
public static void main(String[] args){
/*
* 获取线程对象:static void currentThread()
*/
Thread currentThread = Thread.currentThread(); // 出现在main方法中,当前线程就是主线程
MyThread myThread = new MyThread();
myThread.setName("t1");
System.out.println(myThread.getName());
myThread.start();
for(int i = 0 ; i < 1000 ; i++) {
System.out.println( currentThread .getName() + "--->"+ i);
}
}
}
/*
* 实现Runnable接口,实现run()方法
* 建议使用使用接口这种方式,面向抽象编程,并且可以继承其它的类
*/
class MyThread extends Thread{
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i++) {
Thread currentThread = Thread.currentThread(); // 谁启动就是谁的线程对象
System.out.println(currentThread.getName()+"--->" + i);
}
}
}
线程阻塞
线程阻塞的原因有很多,I/O中断等等,Java人提供了可以让线程进入阻塞的API,sleep,是Thread的静态方法。
当前线程进入阻塞状态:
/*
* 线程的阻塞状态,使用static void sleep(long millis) ,到达时间之后回到就绪状态
* 使当前线程进入阻塞状态
* 进入阻塞状态之后,放弃cpu时间片,让给其他线程使用
* Thread.sleep(long millis),隔离一段时间之后,去执行一段特定的代码
*/
public class Main {
public static void main(String[] args){
/*
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello world");
*/
for(int i = 0 ; i < 10 ; ++i) {
System.out.println(Thread.currentThread().getName() + "--->" + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
笔试题
Thread变量调用sleep:
静态方法使用对象调用会直接转换成类调用。
/*
* 线程的阻塞状态,使用static void sleep(long millis) ,到达时间之后回到就绪状态
* 使当前线程进入阻塞状态
* 进入阻塞状态之后,放弃cpu时间片,让给其他线程使用
* Thread.sleep(long millis),隔离一段时间之后,去执行一段特定的代码
*/
public class Main {
public static void main(String[] args){
Thread t = new MyThread();
t.setName("t");
t.start();
/*try {
t.sleep(1000 * 5); // 对象调用当前线程的会转换为Thread.sleep()
} catch (InterruptedException e) {
e.printStackTrace();
}*/
System.out.println("hello world");
}
}
/*
* 实现Runnable接口,实现run()方法
* 建议使用使用接口这种方式,面向抽象编程,并且可以继承其它的类
*/
class MyThread extends Thread{
@Override
public void run() {
for(int i = 0 ; i < 1000 ; i++) {
Thread currentThread = Thread.currentThread(); // 谁启动就是谁的线程对象
System.out.println(currentThread.getName()+"--->" + i);
}
}
}
唤醒线程
叫醒线程:
/*
* 线程的阻塞状态,使用static void sleep(long millis),如果阻塞时间太长,可以叫醒一个睡眠的线程
* 中断线程的睡眠
*/
public class Main {
public static void main(String[] args){
Thread t = new MyThread();
t.setName("t1");
t.start();
try {
Thread.sleep(1000 * 5);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0 ; i < 10000 ; i++){
t.interrupt(); // 会让线程t中的sleep抛出异常,中断这个线程,达到叫醒线程的目的
}
}
}
/*
* 实现Runnable接口,实现run()方法
* 建议使用使用接口这种方式,面向抽象编程,并且可以继承其它的类
*/
class MyThread extends Thread{
/*
* 子类重写的方法不能比父类的方法多抛出异常
* 父类都没有这种异常,子类却有了,不符合逻辑
*/
/*
@Override
public void run() throws InterruptedException{
for(int i = 0 ; i < 1000 ; i++) {
System.out.println(Thread.currentThread.getName()+"--->" + begin);
Thread.sleep(1000 * 60 * 60 * 24 * 365);
System.out.println(Thread.currentThread.getName()+"--->" + end);
}
}*/
@Override
public void run(){
for(int i = 0 ; i < 100 ; i++) {
System.out.println(Thread.currentThread().getName()+"--->" + "begin");
try {
Thread.sleep(1000 * 60 * 60 * 24 * 360); // 如果在别线程中调用interrupt() ,会让线程此线程中的sleep抛出异常,然后会抓取异常
}catch(InterruptedException e) {
// e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"--->" + "end");
}
}
}
结束进程
结束进程:
使用stop方法就相当于kill掉线程,容易造成数据丢失。
*
* 强行终止线程stop()不建议,已经过时
* 打一个bool标记,通过标记判断
*/
public class Main {
public static void main(String[] args){
MyRunnable r = new MyRunnable();
Thread myThread = new Thread(r);
myThread.setName("t");
myThread.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
/*
* 强行结束,数据丢失
* 过时
*/
// myThread.stop();
r.run = false;
}
}
/*
* 在实现Runnable中打一个bool标记
*/
class MyRunnable implements Runnable{
public boolean run = true;
@Override
public void run() {
if(run) {
for(int i =0 ; i < 10 ; i++) {
System.out.println(Thread.currentThread().getName() + "--->" + i );
System.out.println(run);
if(run == false) {
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
线程让位和合并
当前线程让位 Thread.yield()方法
当前线程阻塞,t1线程加入:t1.join().
线程的调度
一般java程序是采用抢占式调度
java的抢占调度模式:
/*
* java采用的是抢占式调度模型,谁的优先级高,抢到的时间片就高一些/多一些
* 利用方法来设置优先级
* int getPriority() 和 void setPriority(int newPriority)
* 最高优先级10
* 最低优先级1
* 默认优先级5
* 优先级比较高是抢占cpu时间片概率多一些
*
* static void yield() 让位方法,暂停当前线程,回去继续抢占cpu时间片,并不是阻塞,而是从运行状态回到就绪状态,效果不明显
*
* void join() t.join(),当前线程阻塞,t线程执行,直到t线程结束,当前线程才可以开始
*/
public class Main {
public static void main(String[] args){
System.out.println("最高优先级" + Thread.MAX_PRIORITY); // 1
System.out.println("最高优先级" + Thread.NORM_PRIORITY); // 5
System.out.println("最高优先级" + Thread.MIN_PRIORITY); // 10
System.out.println(Thread.currentThread().getName() + "线程优先级:" +Thread.currentThread().getPriority());
MyRunnable r = new MyRunnable();
Thread myThread = new Thread(r);
/*
* 优先级别越高,抢占的时间片的概率就多一点
*/
myThread.setPriority(10);
myThread.setName("t");
myThread.start();
try {
myThread.join(); // myThreaad开始执行,当前线程结束,直到myThead结束
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("main over");
}
}
/*
* 在实现Runnable中打一个bool标记
*/
class MyRunnable implements Runnable{
@Override
public void run() {
for(int i =0 ; i < 1000 ; i++) {
// Thread.yield(); // 暂停此时的线程返回可执行状态
System.out.println(Thread.currentThread().getName() + "--->" + i );
}
}
}
t.join方法的结果:将t线程合并到当前线程,当前线程阻塞,直到t线程结束。
线程同步和互斥
同步和互斥在这篇文章https://blog.csdn.net/liutao43/article/details/114673657
解决互斥的操作一般是给一段代码上锁或者使用PV操作,Java程序解决互斥和同步使用synchronzied关键字来解决互斥和同步。
解决互斥和同步的关键就是找到关键操作,这步操作必须在多线程下,可能会影响最后结果的操作。
synchronized (共享的对象){
关键操作;
}
共享的对象就是多个线程共同使用的对象即可,这里有一个对象锁机制,每一个对象只有一个一个锁,碰见 synchronized (共享的对象)相当于获取锁,别的线程只有等花括号语句执行完毕才能再次获取该对象的锁,否则只能等待或者阻塞,其实这就是OS中的上锁操作,只有这段代码执行完了,才能释放锁。
例如,t1,t2两个线程,t1执行到synchronized (共享的对象)这里,就代表共享的对象的锁被站,t2执行到synchronized (共享的对象)无法获取对象锁,所以就只能等待,这样就解决了线程互斥。
/*
* 线程安全问题
*
* 模拟两个线程对一个账户进行取款,出现线程安全问题
*/
public class Main {
public static void main(String[] args){
Account act = new Account("act-001" , 10000 );
AccountThread t1 = new AccountThread(act);
AccountThread t2 = new AccountThread(act);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
class Account {
private String account;
private double balance;
public Account() {}
public Account(String account, double balance) {
this.account = account;
this.balance = balance;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void withDraw(double money) {
double before = this.getBalance();
double after = before - money;
/*
* t1执行到这里,但还没来得及执行setBalance这行代码,t2线程进来withdraw方法并执行了withDraw方法中的double before = this.getBalance();,此时一定出问题
* t1线程执行完毕,不会出问题
*/
this.setBalance(after);
}
}
class AccountThread extends Thread{
private Account act;
public AccountThread(Account act) {
this.act = act;
}
@Override
public void run() {
double money = 5000;
act.withDraw(money);
System.out.println(Thread.currentThread().getName() + "账户" + act.getAccount() + " 取款成功 , 余额:" + act.getBalance());
}
}
有可能出现两个余额都为5000的现状,就代表获余额并扣钱的时候,这时候变成另一个线程运行,也是获取相应额钱数。
解决互斥
/*
* 线程安全问题
*
* 模拟两个线程对一个账户进行取款,出现线程安全问题
*/
public class Main {
public static void main(String[] args){
Account act = new Account("act-001" , 10000 );
AccountThread t1 = new AccountThread(act);
AccountThread t2 = new AccountThread(act);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
class Account {
private String account;
private double balance;
public Account() {}
public Account(String account, double balance) {
this.account = account;
this.balance = balance;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public void withDraw(double money) {
/*
* 上锁操作
*/
synchronized (this){
double before = this.getBalance();
double after = before - money;
this.setBalance(after);
} // 执行完就释放锁
}
}
class AccountThread extends Thread{
private Account act;
public AccountThread(Account act) {
this.act = act;
}
@Override
public void run() {
double money = 5000;
act.withDraw(money);
System.out.println(Thread.currentThread().getName() + "账户" + act.getAccount() + " 取款成功 , 余额:" + act.getBalance());
}
}
解决方法还可以扩大化,直接在线程类中加synchronized关键字
synchronized (act){
act.withDraw(money);
} // 执行完就释放锁
解决方法还可以在方法加,指定this为唯一参数,获取自己对象的锁,把整个方法上锁
public synchronized void withDraw(double money) {
/*
* 上锁操作
*/
double before = this.getBalance();
double after = before - money;
this.setBalance(after);
}
类锁:这个个上面没关系,类锁只有一种。对象锁相当于有多少各对象就有多少个锁。
public synchronized static void withDraw(double money) {
/*
* 上锁操作
*/
double before = this.getBalance();
double after = before - money;
this.setBalance(after);
}
死锁
死锁:当多进程或者多线程因为硬件资源不足,或者是线程运行永远也等不到继续的条件,这个时候程序就会卡住,一直等下去。
死锁代码:
/*
* 死锁代码要会写
*
* t1抢占了o1的锁,开始睡眠,这时候t2抢占了o2的锁,这时候o1和o2都被占用,就不能被继续抢占了,所以两端程序就永远死锁
*/
public class Main {
public static void main(String[] args)throws Exception{
Object o1 = new Object();
Object o2 = new Object();
Thread t1 = new MyThread1(o1 , o2);
Thread t2 = new MyThread2(o1 , o2);
t1.setName("t1");
t2.setName("t2");
t1.start();
t2.start();
}
}
class MyThread1 extends Thread{
private final Object o1;
private final Object o2;
public MyThread1(Object o1 , Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o1) {
try {
System.out.println(Thread.currentThread().getName() + ": o1 lock");
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + ": o2 lock");
}
}
}
}
class MyThread2 extends Thread{
private final Object o1;
private final Object o2;
public MyThread2(Object o1 , Object o2) {
this.o1 = o1;
this.o2 = o2;
}
@Override
public void run() {
synchronized (o2) {
System.out.println(Thread.currentThread().getName() + ": o2 lock");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
synchronized (o1) {
System.out.println(Thread.currentThread().getName() + ": o1 lock");
}
}
}
}
守护线程
Java语言中的线程有用户线程和守护线程(GC)。
所有的用户线程结束,守护线程自动结束。主线程是一个用户线程。
实现守护线程
视同线程的实例方法setDaemon(true)设置为true
守护线程和定时器:
import java.util.Date;
import java.text.SimpleDateFormat;
import java.util.TimerTask;
public class Main {
public static void main(String[] args) {
Thread t1 = new StoreDataThread();
t1.setName("t1");
t1.setDaemon(true);
t1.start();
for (int i = 0 ; i < 10 ; i++){
try{
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "---->" + i);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
class StoreDataThread extends Thread{
public void run() {
int i = 0;
while(true) {
System.out.println(Thread.currentThread().getName() + "--->" + (++i));
try{
Thread.sleep(1000);
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
}
class LogTimerTask extends TimerTask{
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String strTime = sdf.format(new Date());
System.out.println(strTime + ":成功完成一次数据备份");
}
}
实现线程的第三种方式
实现线程的第三种方式.Callable接口,得到返回值,run方法没有返回值。
实现Callable接口,并重写call方法。将其对象传给FutureTask的构造方法。
import java.util.concurrent.FutureTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
/*
* 实现线程的第三种方式:实现Callable接口
* 这种方式实现的线程可以获取线程的返回值
*
*/
public class Main {
public static void main(String[] args){
// 创建一个未来任务类
FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
public Integer call() throws Exception{ // call方法相当于run方法。
System.out.println("call method begin");
Thread.sleep(1000);
System.out.println("call method end!");
Integer a = 100;
return a;
}
});
Thread t = new Thread(task);
t.start();
/*
* 这是在main的线程中,在主线程中取得t线程的结果.
* get()会导致当前主线程阻塞,必须等待t线程执行完,直到获取最后的结果
*/
Integer i;
try {
i = task.get();
System.out.println(i);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("hello world");
}
}
生产者消费者问题
import javax.swing.*;
import java.util.concurrent.FutureTask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.List;
import java.util.ArrayList;
/*
* 使用wait方法和notify方法实现生产者消费者模式
* 生产线程负责生产 , 消费线程负责消费
* 生产线程和消费线程要达到均衡
*
* wait和notify方法不是线程对象的方法,是普通java对象都有的方法
* wait方法和notify方法建立在线程同步的基础之上.因为线程同时操作一个仓库.有线程安全问题
* wait方法作用:o.wait()是让正在o对象正在执行的当前线程t进入等待状态,并且释放掉t线程之前占有的o对象的锁
* notify方法的作用:o.notify()让正在o对象上等待的线程唤醒,只是通知,不会释放o对象上之前占有的锁
*
*
* 模拟:
* 仓库用List集合
* 1个代表仓库满了,0表示仓库空了
*/
public class Main {
public static void main(String[] args){
List list = new ArrayList();
Thread t1 = new Thread(new Prodeucer(list));
Thread t2 = new Thread(new Consumer(list));
t1.setName("生产者线程");
t2.setName("消费者线程");
t1.start();
t2.start();
}
}
class Prodeucer implements Runnable{
private List list;
public Prodeucer(List list) {
this.list = list;
}
@Override
public void run() {
int i = 0;
while(i <= 10) {
synchronized (list) {
if(list.size() > 0 ) { // 仓库中有元素
try {
// 满了,需要等待,并且释放掉Producer之前占有的list锁
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 到这里残酷肯定是空,要进行生产
Object obj = new Object();
list.add(obj);
System.out.println(Thread.currentThread().getName()+ "--->" + obj);
// 唤醒消费者进行消费
list.notifyAll();
}
i++;
}
}
}
class Consumer implements Runnable{
private List list;
public Consumer(List list) {
this.list = list;
}
@Override
public void run() {
int i = 0;
while(i <= 10) {
synchronized (list) {
if(list.size()== 0) {
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object obj = list.remove(0);
System.out.println(Thread.currentThread().getName()+ "--->" + obj);
// 唤醒生产者生产
list.notifyAll();
// list.notifyAll();
}
i++;
}
}
}