基础知识:Java多线程编程

关于线程和进程

   

线程是进程的一个执行单元,它和进程一样拥有独立的执行控制,由操作系统负责调度,它们俩的区别可以用一句话概括之,那就是进程是程序的一次执行,而线程可以理解为进程执行的一段程序片段,也就是说它们是一种包含关系,线程不能独立运行,必须依存在进程之中

   

另外从资源分配的角度上看,进程是系统进行资源分配和调度的一个独立单位,而线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。

   

进程间通信和线程间通信的区别

   

在通信方面也是,进程间通信比较困难,因为进程之间相互独立,而线程之间共享一块内存区域,通信方便

   

进程间通信主要包括管道, 系统IPC(包括消息队列,信号量,共享存储), SOCKET.

而进程间通信主要包括共享内存,比如经典的生产者和消费者通过共享内存的方式进行通信;它们共享的内存是:SyncStack对象;生产者通过SyncStack的同步方法pop向其中添加对象;消费者通过SyncStack的同步方法pop方法在SyncStack对象中获取对象;这是对象间共享内存(或共享数据区域)的方式进行的通信。

   

零散补充

   

另外多说一句一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。

   

让一个类成为线程类

   

让一个类成为线程类有两种方式,一种是实现java.lang.Runnable接口,另外一种是基础java.lang.Thread

   

Runnable接口和Thread类的区别

   

它们的区别在于三点,一是Thread类中还提供了额外的方法(其实Thread也是实现了Runnable接口的),二是从继承的角度看,接口比类更灵活,因为只能继承一个类而可以继承多个接口,三是如果类实现Runnable接口,那么当调用这个线程的对象开辟多个线程时,可以让这些线程调用同一个变量,如果类继承Thread的话,那么需要用到内部类来实现类似的功能,原理就是利用内部类可以访问任意外部变量这一特性

   

如何启动一个线程

   

通过以上两种方法实现一个线程类之后,线程的实例并没有被创建,所以也没有运行起来,要启动它,就要调用Threadstart方法,注意这里并不是run方法(既不是继承Thread类重写的run方法,也不是实现Runnable接口的run方法,run方法中包含的是线程要干的事儿的主体,跟线程的启动没有关系)

   

继承Thread的线程类的启动

   

Thread threadTest=new ThreadTest();

threadTest.start();

   

实现Runnable的线程类的启动

   

Thread thread=new Thread(new RunnableTest());

thread.start();

   

synchronized保证线程间同步

   

如果不用synchronized同步

   

class MyThread extends Thread {

public static int index;

public void run() {

for (int i = 0; i < 100; i++) {

System.out.println(getName() + ":" + index++);

}

}

}

   

public static void main(String[] args) {

new MyThread().start();

new MyThread().start();

new MyThread().start();

   

}

   

输出结果

   

Thread-1:13

Thread-2:16

Thread-0:15

Thread-2:18

Thread-1:17

Thread-2:20

Thread-0:19

   

synchronized同步

   

synchronized同步原理

   

synchronized同步的原理是每个对象都有一个线程锁,synchronized可以用任一对象的线程锁来锁住一段代码,任何想进入该代码段的线程必须在解锁以后才能执行,只有占用该锁资源的线程执行完毕,该锁资源才被释放,其他线程才能进入

   

class MyThread extends Thread {

public static int index;

public static Object        object=new Object();

public void run() {

synchronized (object) {

for (int i = 0; i < 100; i++) {

System.out.println(getName() + ":" + index++);

}

}

}

}

   

另外除了把synchronized加在对象上,也可以加在方法上,这叫做同步方法,其实这时候的锁是加在this所引用的对象上的

   

生产者消费者模型

   

首先主类Store,相当于线程要争夺的资源,定义一个最大值,以及一个现有值,构造函数中将最大值作为参数传进去,并且将现有值赋值为0

   

接下来就是增加和删除两个方法,分别用synchronized标记

   

在增加方法中,如果满了,则调用thiswait方法,不然的话就现有值增加,然后通知其他线程

   

在删除方法中,如果空了,则调用thiswait方法,不然的话就现有值减少,然后通知其他线程

   

public class Store {

private final int MAX_SIZE;

private int curCount;

public Store(int n){

MAX_SIZE=n;

curCount=0;

}

public synchronized void add(){

while (curCount>MAX_SIZE) {

System.out.println("仓库满了");

try {

this.wait();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

curCount++;

System.out.println(Thread.currentThread().toString()+"put"+curCount);

this.notifyAll();

}

public synchronized void remove() {

while(curCount<0){

System.out.println("仓库是空的");

try {

this.wait();

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

System.out.println(Thread.currentThread().toString()+"get"+curCount);

curCount--;

this.notify();

}

}

   

然后就是要创建生产者类和消费者类,它们都要是线程类,也就是继承Thread

   

两个线程类要和公共资源联系起来,就需要在两个类中分别定义一个公共资源的成员变量,然后在构造函数中传入进来赋值

   

生产者线程类的run方法中为永真循环,不停的添加,添加一个,让线程睡1

   

消费者线程类的run方法中为永真循环,不停的删除,删除一个,让线程睡1.5

   

class Producer extends Thread{

private Store store;

Producer(Store store){

this.store=store;

}

public void run() {

while (true) {

store.add();

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

class Consumer extends Thread{

private Store store;

Consumer(Store store)

{

this.store=store;

}

public void run() {

while (true) {

store.remove();

try {

Thread.sleep(1500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

   

然后再在main函数中建立这些线程和启动线程

   

Store store=new Store(5);

Thread producer1=new Producer(store);

Thread producer2=new Producer(store);

Thread consumer1=new Consumer(store);

Thread consumer2=new Consumer(store);

producer1.setName("pro1");

producer2.setName("pro2");

consumer1.setName("con1");

consumer2.setName("con2");

producer1.start();

producer2.start();

consumer1.start();

consumer2.start();

   

使用Java线程池

   

线程池属于对象池,对象池使用的基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。

   

并非所有对象都适合拿来池化――因为维护对象池也要造成一定开销。

   

线程池所对应的类是java.util.concurrent

   

线程池的构造方法

   

ThreadPoolExecutor

(

int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue,

RejectedExecutionHandler handler

   

)

   

corePoolSize 线程池维护线程的最少数量 core : 核心)

maximumPoolSize:线程池维护线程的最大数量

keepAliveTime 线程池维护线程所允许的空闲时间

unit 线程池维护线程所允许的空闲时间的单位

workQueue 线程池所使用的缓冲队列

handler 线程池对拒绝任务的处理策略

   

一个任务通过 execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是 Runnable类型对象的run()方法。

   

利用Execute执行器来管理线程对象

   

不必显式的管理线程的生命周期,可以使用Executors的静态方法newFixedThreadPool 或者newCachedThreadPool来创建ExecutorService对象;

   

同样也是调用ExecutorService对象的execute方法来添加线程到线程池

   

 

原文地址:https://www.cnblogs.com/keedor/p/4492255.html