多线程(Multi—Thread)
线程与进程的区别:
多个线程的内部数据和状态都是完全独立的,而多线程是共享一块内存空间和一组系统资源,有可能相互影响。
线程本身的数据只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。
多线程是指在单个程序中可以同时运行多个不同的线程执行不同的任务。
线程是程序类的顺序控制流,只能使用分配给程序的资源和环境。
多线程编程的目的,就是“最大限度地利用CPU资源”,当某一线程的处理不需要占用CPU而只和I/o等资源打交道时,让需要占用CPU资源的其他线程有机会获得CPU资源。从根本上上说,这既是多线程编程的最终目的。
java中如果我们没有自己产生线程,那么系统就会给我们产生一个线程(主线程,main方法就在主线程上运行)我们的程序都是有线程来执行的。
进程:执行中的程序(程序是静态的概念,进程是动态的概念)
一个进程可以包含一个或多个线程
一个程序实现多个代码同时交替运行就需要产生多个线程
CPU随机的抽出时间,让我们的程序一会做这件事情,一会做那件事情。
线程的实现:在java中通过run方法为线程指明要完成的任务,有两种技术来为线程提供run方法。
1、继承Thread类并重写run方法。
2、通过定义实现Runnable接口进而实现run方法。
将我们希望的线程执行的代码放在run方法中,然后通过start方法来启动线程,start方法首先为线程的执行准备好系统资源,然后再去调用run方法。当某个类继承了Thread类之后,该类就为一个线程类。
public class ThreadTest {
public static void main(String[] args) {
Thread1 t=new Thread1();
Thread2 t1=new Thread2();
t.start();
t1.start();
}
}
class Thread1 extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("hello "+i);
}
}
}
class Thread2 extends Thread{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("world "+i);
}
}
}
一个进程至少包括一个线程。
对于单核CPU来说,某一时刻只能有一个线程在执行(微观串行),从宏观角度来看,多个线程在同时执行(宏观并行)
对于双核及以上的CPU来说,可以做到真正的微观并行。
我们必须要调用start方法才是启动了我们的线程类。
实现runnable接口实现线程
public class ThreadTest {
public static void main(String[] args) {
Thread t=new Thread(new Thread1());
t.start();
}
}
class Thread1 implements Runnable{
@Override
public void run() {
for (int i=0;i<100;i++){
System.out.println("hello "+i);
}
}
}
当生成一个线程对象时,如果没有为其设定名字,线程的名字会将使用:Thread-number(Threadone)并且自动增加被所有的Thread对象共享。
当使用第一种方式来生成线程对象时,我们需要重写run方法,因为Thread类的run方法什么都不做。
当使用第二种方式来生成线程对象时,我们需要实现Runnale接口的run方法,然后使用new Thread(new MyThrea的())(假如MyThread已经实现了Runnablej接口)来生成线程对象,这时候线程对象的run方法或调用MyThread类的run方法,这样我们自己编写的run方法就可以执行了。
线程的生命周期:创建状态,可运行状态,不可运行状态,消亡状态。
停止线程的方式:不能使用Thread的stop方法来终止线程的执行,一般定义一个变量,在run方法中是一个循环,循环每次检查该变量,如果满足条件则继续执行,否则跳出循环,线程结束 。
不能依靠线程优先级来决定线程的执行顺序。
多线程的同步机制:在多线程环境中,可能会有两个或者更多的线程去占用同一个资源
public class FetchMoney {
public static void main(String[] args) {
Bank bank = new Bank();
Thread t1 = new MoneyThread(bank);
Thread t2 =new MoneyThread(bank);
t1.start();
t2.start();
}
}
class Bank{
private int money=1000;
public int getMoney(int number) throws InterruptedException {
if (number<0){
return -1;
}
else if (number>money){
return -2;
}
else if (money<0){
return -3;
}
else {
try {
Thread.sleep(1000);
}
catch (Exception e){
e.printStackTrace();
}
money-=number;
System.out.println(money);
return number;
}
}
}
class MoneyThread extends Thread{
private Bank bank;
public MoneyThread(Bank bank){
this.bank=bank;
}
@Override
public void run() {
try {
System.out.println(bank.getMoney(800));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
synchronized关键字:当synchronized关键字修饰一个方法时候,该方法叫做同步方法。
java中的每一个对象都有一个锁(lock)或者叫做监视器(monitor),当访问某一个对象的synchronized方法时,表示将该对象上锁,此时其他任何线程都无法再去访问该synchronized方法了,知道之前的那个线程执行方法完毕后(或者抛出异常),那么将该对象的锁释放掉,其他线程才有可能在去。
public class ThreadTest4 {
public static void main(String[] args) {
Example example=new Example();
Thread t1=new TheThread(example);
Thread t2=new TheThread(example);
t1.start();
t2.start();
}
}
class Example{
public synchronized void execute()
{
for (int i=0;i<50;i++){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("example"+i);
}
}
}
class TheThread extends Thread{
private Example example;
public TheThread(Example example){
this.example=example;
}
@Override
public void run() {
this.example.execute();
}
}
如果一个对象有多个synchronized方法,在某一时刻线程进入到了某个synchronized方法。那么在该方法执行完之前,其他线程是无法进行的。
如果某个synchronized方法是static的,那么当线程访问该方法是,他锁住的不是synchronized方法所在的对象,而是synchronized所在的对象对应的Class对象,因为java中无论一个类有多少个对象,这些对象会对应唯一一个Class对象,因此当线程分别访问同一个类的两个对象的static,synchronized方法是,他们的执行的顺序也是顺序的,也即是说一个线程先去执行方法,执行完毕后另一个线程才开始执行。
synchronized块写法:
synchronized(object){}
表示在线程执行的时候会对object对象上锁
public class ThreadTest5 {
public static void main(String[] args) {
Example2 example2=new Example2();
TheThread3 t1=new TheThread3(example2);
TheThread4 t2=new TheThread4(example2);
t1.start();
t2.start();
}
}
class Example2{
public Object object=new Object();
public void execute()
{
synchronized (this) {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("example" + i);
}
}
}
public void execute1()
{
synchronized (this) {
for (int i = 0; i < 50; i++) {
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("hello" + i);
}
}
}
}
class TheThread3 extends Thread{
private Example2 example;
public TheThread3(Example2 example){
this.example=example;
}
@Override
public void run() {
this.example.execute();
}
}
class TheThread4 extends Thread{
private Example2 example;
public TheThread4(Example2 example){
this.example=example;
}
@Override
public void run() {
this.example.execute1();
}
}
synchronized方法是一种粗粒度的并发控制,某一时刻,只有一个线程执行该synchronized方法;synchronized块则是一种细粒度的并发控制,只会讲块中的代码同步,位于方法内,synchronized块之外的代码是可以被多个线程同时访问到。
被synchronized保护的数据应该是私有的。
notify,wait都是在Object类中的,不能被重写的方法
package thread;
public class Sample {
private int number;
public synchronized void increase(){
if (number!=0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(number);
notify();
}
public synchronized void decrease(){
if (number==0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(number);
notify();
}
}
class IncreaseThread extends Thread{
private Sample sample;
public IncreaseThread(Sample sample){
this.sample=sample;
}
@Override
public void run() {
for (int i=0;i<20;i++) {
try {
Thread.sleep((long)(Math.random()*1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
sample.increase();
}
}
}
class DecreaseThread extends Thread{
private Sample sample;
public DecreaseThread(Sample sample){
this.sample=sample;
}
@Override
public void run() {
for (int i=0;i<20;i++) {
try {
Thread.sleep((long)(Math.random()*1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
sample.decrease();
}
}
}
class Test{
public static void main(String[] args) {
Sample sample=new Sample();
Thread t1=new IncreaseThread(sample);
Thread t2=new DecreaseThread(sample);
t1.start();
t2.start();
}
}