您的位置:首页 > 编程语言 > Java开发

Java并发编程(一)

2016-02-26 11:56 706 查看
多线程比多任务更加有挑战,多线程是同一个程序内部并行执行,因此会对相同的内存空间进行并发读写,其中的一些错误未必会在单cpu上出现,但是现在随着cpu多核的出现,也就意味着不同的线程能被不同的cpu核得到真正意义的并行执行。任何结果都会产生,甚至不能预测,所以结果也不能确定。

一、Java中实现多线程的方式

1.Thread

public class ThreadDemo extends Thread{
private int tick=5;
@Override
public void run() {
for (int i = 0; i < 10; i++) {
if(tick>0){
System.out.println(tick--);
}
}
}
public static void main(String[] args) {
new ThreadDemo().start();
new ThreadDemo().start();
}
}

2.Runnable

public class RunnableDemo implements Runnable{
private int tick=5;
public void run() {
for (int i = 0; i < 10; i++) {
if(tick>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(tick--);
}
}
}
public static void main(String[] args) {
RunnableDemo rd=new RunnableDemo();
new Thread(rd).start();
new Thread(rd).start();
new Thread(rd).start();
}
}

一般情况下我们都会采用第二种方式实现多线程。但是这种方法是非原子性的。所以在实现的时候要加入多个同步(互斥锁)

public class RunnableDemo implements Runnable{
private int tick=5;
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (this) {
if(tick>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(tick--);
}
}
}
}
public static void main(String[] args) {
RunnableDemo rd=new RunnableDemo();
new Thread(rd).start();
new Thread(rd).start();
new Thread(rd).start();
}
}

线程终止:interrupt

public class RunnableDemo implements Runnable{

public void run(){
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
System.out.println("线程没有终止竟然运行完了");
}

public static void main(String[] args) {
RunnableDemo rd=new RunnableDemo();
Thread t=new Thread(rd);
t.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();//中断线程
System.out.println("end");
}
}

这段代码当线程调用了interrupt方法之后如果不在catch中return线程是不会终止的,还会运行。return之后线程正常终止。但是当我们不使用休眠的时候看一下代码,发现程序并没有终止。

public class RunnableDemo implements Runnable{
String str="";
public void run() {
try {
for (int i = 0; i < 1000000; i++) {
str+=i;
}
} catch (Exception e) {
e.printStackTrace();
return;
}
System.out.println("hahah ");
}

public static void main(String[] args) {
RunnableDemo rd=new RunnableDemo();
Thread t=new Thread(rd);
t.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();//中断线程
System.out.println("end");
}
}

真正的终止一个线程最好的方法是推荐“共享变量”的方式volatile变脸表示这个变量在一个线程中使用的时候有可能被另一个变量修改

public class RunnableDemo implements Runnable{
volatile static boolean stop = false;
String str="";
public void run() {
try {
for (int i = 0; i < 1000000; i++) {
str+=i;
if (stop) {
return;
}
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("hahah ");
}

public static void main(String[] args) {
RunnableDemo rd=new RunnableDemo();
Thread t=new Thread(rd);
t.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//t.interrupt();//中断线程
stop=true;
System.out.println("end");
}
}

随后发现interrupt是抛出一个终端信号,并不会真正的停止线程,如果发生阻塞最好是即使用共享变量 有使用interrupt,当socket发生阻塞的时候调用socket.close方法

public class RunnableDemo implements Runnable{
volatile static boolean stop = false;
String str="";
public void run() {
try {
for (int i = 0; i < 1000000; i++) {
str+=i;
if (stop) {
return;
}
}
} catch (Exception e) {
e.printStackTrace();
return;
}
System.out.println("hahah ");
}

public static void main(String[] args) {
RunnableDemo rd=new RunnableDemo();
Thread t=new Thread(rd);
t.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();//中断线程
System.out.println("我要终止线程");
stop=true;
System.out.println("end");
}
}

t.isInterrupted():判断线程的终止状态

join:方法用线程对象调用,如果在一个线程A中调用线程Bjoin,线程A将会等待线程B执行完毕后在执行

yield:可以直接用Thread调用,当前线程让出cpu给相同级别的线程。如果没有则继续执行

线程的挂起与重启:

API中包含这两个API已经被淘汰,因为他们是不安全的,如果是线程锁定的时候挂起 会产生死锁,长时间计算时候也可能导致问题

suspend:挂起

resume:重启

可以采用共享变量+Thread.sleep的方式和java中的通知等待机智实现。

Java中有两类线程:

User Thread(用户线程)

运行在前台

Daemon Thread(守护线程)

运行在后台

必须在start之前调用,可以使用Thread.setDaemon(true)实现,在守护线程中产生的新线程也是守护线程,不要讲读写,逻辑计算放入守护线程

public class RunnableDemo implements Runnable{
public void run() {
class DeaThread implements Runnable{
public void run() {
for(int i=0;i<100;i++){
System.out.println(i);
}
}
}
Thread t=new Thread(new DeaThread());
//t.setDaemon(true);
t.start();
}

public static void main(String[] args) {
RunnableDemo rd=new RunnableDemo();
Thread t=new Thread(rd);
//t.setDaemon(true);
t.start();
System.out.println("end");
}
}

线程阻塞的几种状态:

Thread.sleep

wait

i/o产生阻塞

锁阻塞

Volatile关键字:

变量可能被保存在本地内存(寄存器)而不是直接从主存中读取,这可能造成数据的不统一。这个就告诉jvm这个变量要从主存读取。不能保存他的私有拷贝,他是一种比Sync更加轻量级的同步机制,如果变量已经在sync代码快或者为常亮时可以忽略使用。





synchronized

为了避免多个线程访问统一资源(临界资源)时发生数据不一致,我们会采取同步机制,以确保在某一时刻,方法内只允许有一个线程

采用synchroized修饰符实现的同步机制叫做互斥锁机制,他所获得的锁叫做互斥锁。每个对象都有一个所标记

1.如果同一个方法内同时有两个或更多线程,则每个线程有自己的局部变量拷贝。

2.类的每个实例都有自己的对象级别锁,当一个线程方位实例对象中的synchronized同步代码块或同步方法时,该线程便获取了该实例的对象级别锁,其他线程这时如果要访问synchronized同步代码块或同步方法,便需要阻塞等待,直到前面的线程从同步代码块或同步方法中退出,释放掉该对象级别所。

3.访问同一个类的不同实例对象中的同步代码块,不存在阻塞等待获取对象锁的问题,因为他们获取的是各自实例对象级别的锁,相互之间没有影响。

4.共用同一个对象,也就是共用同一个对象同步锁,那么其他线程调用其他同步代码块时也无法执行,只能执行非synchronize标记的

public class ThreadDemo implements Runnable{

public synchronized void sell(){
System.out.println("得到锁");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("释放所");
}

public synchronized void say(){
System.out.println("hello");
}

public static void main(String[] args) {
final ThreadDemo td=new ThreadDemo();
new Thread(new Runnable() {
public void run() {
new Thread(td).start();
}
}).start();

new Thread(new Runnable() {
public void run() {
td.say();
}
}).start();
}

public void run() {
sell();
}
}

5.当对象级别锁为this时便获取的是当前对象的对象级别锁,当对象级别锁为ThreadDemo.class 时,那么他对所有实例的也是用ThreadDemo.class互斥

它控制所有static变量和所有static 方法的并发访问,同步方法是加在this上的

public class ThreadDemo implements Runnable{

public  void sell(){
synchronized (ThreadDemo.class) {
System.out.println("得到锁");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("释放所");
}
}

public  void say(){
synchronized (ThreadDemo.class) {
System.out.println("hello");
}

}

public static void main(String[] args) {
final ThreadDemo td=new ThreadDemo();
final ThreadDemo td2=new ThreadDemo();
new Thread(new Runnable() {
public void run() {
new Thread(td).start();
}
}).start();

new Thread(new Runnable() {
public void run() {
td2.say();
}
}).start();
}

public void run() {
sell();
}
}

把另一个换成this发现就不互斥了

Synchronize:另一个作用内存可见性

我们防止某个线程正在使用而另一线程同时修改该状态,而且还希望一个线程修改了状态另一个线程可以看到该变化。而线程同步恰恰实现了这一点。

实现内存可见性的两种方法比较:synchronize和volatile 使用synchronize更安全些,synchronize即可以保证可见性,又可以保证原子性,而volatile

声名的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用比如count++,count=count+1.

多线程环境下安全使用集合

在集合API中最初设计的Vector和Hashtable是多线程安全的。只在添加和删除上加了同步,在所有修改上都没有添加同步

当多线成使用它时为了是他多线程安全,必须采取额外的措施。





下面是安全便利线程的事例

public class Test  {
public static void main(String[] args) {
List<String> list=Collections.synchronizedList(new ArrayList<String>());
list.add("1");
list.add("2");
list.add("3");
synchronized (list) {
Iterator<String> iter=list.iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
}
}
}

遵循以下规则避免死锁:

1.只有在必要最短时间内持有锁,考虑使用同步语句块代替整个同步方法:

2.尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁是尽量短暂。

3.创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁。

锁的重入:

同一线程是可重入的,重入意味着获取锁的粒度是线程,而不是调用。

wait、notify、notifyAll

wait:该方法用来将当前线程置入休眠状态,直到接到通知或被中断为止。只能在同步中调用

notify:该法发也要在同步中调用,通知等待中的任意一个wait 并且退出同步之后wait线程才能执行

notifyall:所有wait会竞争,竞争到的执行

notify的遗漏:即当threadA没有wait的时候ThreadB已经notify了,那么就会出现遗漏,可以加入while循环判断变量的状态之后再notify

notifyall 早期通知的问题:解决办法还是while 方式

生产者-消费者能够模型:

package mybatis.model;

public class Info {
private String name="name";
private String content="content";
private boolean flag=true;
public synchronized void set(String name,String content){
while(!flag){
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
this.setName(name);
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.setContent(content);
flag=false;
super.notify();
System.out.println("setsetset");
}

public synchronized void get(){
while (flag) {
try {
super.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(this.getName()+"---》"+this.getContent());
flag=true;
super.notify();

}

public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public boolean isFlag() {
return flag;
}
public void setFlag(boolean flag) {
this.flag = flag;
}

}

package mybatis.model;

public class Producer implements Runnable {
private Info info=null;
int i = 0;
public Producer(Info info){
this.info=info;
}
public void run() {
synchronized (Producer.class) {
boolean flag=true;
for (; i < 10; i++) {
if(flag){
this.info.set("姓名--1", "内容--1");
flag=false;
}else{
this.info.set("姓名二", "内容二");
flag=true;
}
}
}
}
}

package mybatis.model;

public class Consumer implements Runnable {
private Info info=null;
int i = 0;
public Consumer(Info info) {
this.info=info;
}

public void run() {
synchronized (Consumer.class) {
for (; i < 10; i++) {
this.info.get();
}
}
}
}


Executor接口中定义了一个方法execute,该方法接收一个Runable 实例,我们一般用这个接口管理和实现多线程

Executors 提供了一系列工场方法用于创建线程池,返回的线程池都实现了ExecutorService接口

public class TestCacheThreadPool {
public static void main(String[] args) {
ExecutorService executorService=Executors.newCachedThreadPool();
//        ExecutorService executorService=Executors.newFixedThreadPool(5);
//        ExecutorService executorService=Executors.newSingleThreadExecutor();
for(int i=0;i<5;i++){
executorService.execute(new TestRunnable());
System.out.println(i);
}
executorService.shutdown();
}
}
class TestRunnable implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+"线程被调用了");
}
}

public class CallAbleTest {
public static void main(String[] args) {
ExecutorService executorservice=Executors.newCachedThreadPool();
List<Future<String>> list=new ArrayList<Future<String>>();
for(int i=0;i<10;i++){
TestCallAble tc=new TestCallAble();
tc.setStr(i+"**********");
tc.setStr2(i+"**********");
Future<String> fut=executorservice.submit(tc);
list.add(fut);
}
try{
for(Future<String> ft:list){
System.out.println(ft.get());
}
}catch(Exception e){
e.printStackTrace();
}finally{
executorservice.shutdown();
}
}
}
class TestCallAble implements Callable<String>{
private String str;
private String str2;

public String getStr2() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return str2;
}

public void setStr2(String str2) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.str2 = str2;
}

public String getStr() {
return str;
}

public void setStr(String str) {
this.str = str;
}

public String call() throws Exception {
return getStr()+"杨松"+getStr2();
}

}

自定义线城池

public class ThreadPoolTest {
public static void main(String[] args)  {
//创建等待队列
BlockingQueue<Runnable> bq=new LinkedBlockingQueue<Runnable>();
//创建线程池
ThreadPoolExecutor tpe=new ThreadPoolExecutor(3, 5, 60, TimeUnit.SECONDS, bq);
for(int i=0;i<5;i++){
MyThread mt=new MyThread();
tpe.execute(mt);
}
tpe.shutdown();
}
}
class MyThread implements Runnable{
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行");
try{
Thread.sleep(100);
}catch(Exception e){
e.printStackTrace();
}
}
}

Lock锁 和条件变量

Lock接口,他提供了比synchronize更加广泛的所操作





public class LockDemo implements Runnable {
int i = 10;
Lock lock = new ReentrantLock();
public static void main(String[] args) {
LockDemo ld = new LockDemo();
new Thread(ld).start();
new Thread(ld).start();
new Thread(ld).start();
new Thread(ld).start();
new Thread(ld).start();
new Thread(ld).start();
}

public void test() {
// 默认使用非公平锁,如果要使用公平锁,需要传入参数true
lock.lock();
try {
for (; i >= 0; i--) {
System.out.println(i);
}
} finally {
lock.unlock();
}
}

public void run() {
test();
}
}

Condition await:挂起 signal 启动线程

读写锁

ReadWriteLock

rwl.writeLock().lock() //获取写锁

rwl.readLock.lock() //获取读锁

阻塞队列

BlockingQueue 接口

阻塞栈

BlockingDeque

障碍器:CyclicBarrier

先要执行一组任务,执行完这一组任务之后才能执行这个任务

信号量:

Semaphore

我们完全可以通过使用信号量来自定义实现类似java中synchronized、wait、notify机智

Semaphore仅仅对资源的并发访问任务数进行监控,而不会保证线程安全,因此,在访问的时候,要自己控制线程安全
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: