并发编程

Posted zqlovesym

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程相关的知识,希望对你有一定的参考价值。

1、线程基础、线程之间的共享和协作

 

基础概念

CPU核心数和线程数的关系

核心数:线程数=1:1  ;使用了超线程技术后---> 1:2

 

CPU时间片轮转机制

又称RR调度,会导致上下文切换

 

什么是进程和线程

进程:程序运行资源分配的最小单位,进程内部有多个线程,会共享这个进程的资源

线程:CPU调度的最小单位,必须依赖进程而存在。

 

澄清并行和并发

并行:同一时刻,可以同时处理事情的能力

并发:与单位时间相关,在单位时间内可以处理事情的能力

 

高并发编程的意义、好处和注意事项

好处:充分利用cpu的资源、加快用户响应的时间,程序模块化,异步化

问题:

线程共享资源,存在冲突;

容易导致死锁;

启用太多的线程,就有搞垮机器的可能

认识Java里的线程

新启线程的方式

三种

1.继承Thread类,2.实现runable接口(无返回值)  3.实现callable接口 (有返回值)

举例:

 

package com.xiangxue.ch1;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 *@author
 *
 *类说明:如何新建线程
 */
public class NewThread {
    /*扩展自Thread类*/
    
    /*实现Runnable接口*/
    private static class UseRun implements Runnable{

        @Override
        public void run() {
            System.out.println("I am implements Runnable");
        }
        
    }
    
    /*实现Callable接口,允许有返回值*/
    private static class UseCall implements Callable<String>{

        @Override
        public String call() throws Exception {
            System.out.println("I am implements Callable");
            return "CallResult";
        }
        
    }    
    
    public static void main(String[] args) 
            throws InterruptedException, ExecutionException {
        
        UseRun useRun = new UseRun();
        new Thread(useRun).start();
        Thread t = new Thread(useRun);
        t.interrupt();
        
        UseCall useCall = new UseCall();
        FutureTask<String> futureTask = new FutureTask<>(useCall);
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
    }
}

 

 

 

java本身就是多线程:

 

package com.xiangxue.ch1;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

/**
 *@author
 *
 *类说明:java的多线程无处不在
 */
public class OnlyMain {
    public static void main(String[] args) {
        //虚拟机线程管理的接口
        ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
        ThreadInfo[] threadInfos =
                threadMXBean.dumpAllThreads(false, false);
        for(ThreadInfo threadInfo:threadInfos) {
            System.out.println("["+threadInfo.getThreadId()+"]"+" "
                    +threadInfo.getThreadName());
        }
    }
}





"C:Program FilesJavajdk1.8.0_73injava" "-javaagent:D:DownloadsIntelliJ IDEA 2018.1libidea_rt.jar=52049:D:DownloadsIntelliJ IDEA 2018.1in" -Dfile.encoding=UTF-8 -classpath "D:a_享学享学代码ppt1、第一期课程 ppt 资料(01)并发编程1-2018.05.06-第一节课vip-concurrentin;C:Program FilesJavajdk1.8.0_73jrelibcharsets.jar;C:Program FilesJavajdk1.8.0_73jrelibdeploy.jar;C:Program FilesJavajdk1.8.0_73jrelibextaccess-bridge-64.jar;C:Program FilesJavajdk1.8.0_73jrelibextcldrdata.jar;C:Program FilesJavajdk1.8.0_73jrelibextdnsns.jar;C:Program FilesJavajdk1.8.0_73jrelibextjaccess.jar;C:Program FilesJavajdk1.8.0_73jrelibextjfxrt.jar;C:Program FilesJavajdk1.8.0_73jrelibextlocaledata.jar;C:Program FilesJavajdk1.8.0_73jrelibext
ashorn.jar;C:Program FilesJavajdk1.8.0_73jrelibextsunec.jar;C:Program FilesJavajdk1.8.0_73jrelibextsunjce_provider.jar;C:Program FilesJavajdk1.8.0_73jrelibextsunmscapi.jar;C:Program FilesJavajdk1.8.0_73jrelibextsunpkcs11.jar;C:Program FilesJavajdk1.8.0_73jrelibextzipfs.jar;C:Program FilesJavajdk1.8.0_73jrelibjavaws.jar;C:Program FilesJavajdk1.8.0_73jrelibjce.jar;C:Program FilesJavajdk1.8.0_73jrelibjfr.jar;C:Program FilesJavajdk1.8.0_73jrelibjfxswt.jar;C:Program FilesJavajdk1.8.0_73jrelibjsse.jar;C:Program FilesJavajdk1.8.0_73jrelibmanagement-agent.jar;C:Program FilesJavajdk1.8.0_73jrelibplugin.jar;C:Program FilesJavajdk1.8.0_73jrelib
esources.jar;C:Program FilesJavajdk1.8.0_73jrelib
t.jar;D:a_享学1--并发编程2.线程的并发工具类 (1)commons-lang3-3.9commons-lang3-3.9.jar;D:a_享学1--并发编程2.线程的并发工具类 (1)commons-lang3-3.9-bin.zip" com.xiangxue.ch1.OnlyMain
[6] Monitor Ctrl-Break
[5] Attach Listener
[4] Signal Dispatcher
[3] Finalizer
[2] Reference Handler
[1] main

Process finished with exit code 0

 

 

 

怎么样才能让Java里的线程安全停止工作呢

线程自然终止:自然执行完或抛出未处理异常

stop()resume(),suspend()已不建议使用,stop()会导致线程不会正确释放资源,suspend()容易导致死锁。

java线程是协作式,而非抢占式

调用一个线程的interrupt() 方法中断一个线程,并不是强行关闭这个线程,只是跟这个线程打个招呼,将线程的中断标志位置为true,线程是否中断,由线程本身决定。

isInterrupted() 判定当前线程是否处于中断状态。

static方法interrupted() 判定当前线程是否处于中断状态,同时中断标志位改为false

方法里如果抛出InterruptedException线程的中断标志位会被复位成false,如果确实是需要中断线程,要求我们自己在catch语句块里再次调用interrupt()

举例:

 

package com.xiangxue.ch1.safeend;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 *@author Mark老师   享学课堂 https://enjoy.ke.qq.com 
 *
 *类说明:抛出InterruptedException异常的时候,要注意中断标志位
 */
public class HasInterrputException {
    
    private static SimpleDateFormat formater 
        = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss_SSS");
    
    private static class UseThread extends Thread{
        
        public UseThread(String name) {
            super(name);
        }
        
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while(!isInterrupted()) {
                try {
                    System.out.println("UseThread:"+formater.format(new Date()));
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    System.out.println(threadName+" catch interrput flag is "
                            +isInterrupted()+ " at "
                            +(formater.format(new Date())));
                    interrupt();
                    e.printStackTrace();
                }
                System.out.println(threadName);                
            }
            System.out.println(threadName+" interrput flag is "
                    +isInterrupted());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread endThread = new UseThread("HasInterrputEx");
        endThread.start();
        System.out.println("Main:"+formater.format(new Date()));
        Thread.sleep(800);
        System.out.println("Main begin interrupt thread:"+formater.format(new Date()));
        endThread.interrupt();
        

    }

}

 

package com.xiangxue.ch1.safeend;

/**
 *@author Mark老师   享学课堂 https://enjoy.ke.qq.com 
 *
 *类说明:如何安全的中断线程
 */
public class EndThread {
    
    private static class UseThread extends Thread{
        
        public UseThread(String name) {
            super(name);
        }
        
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            while(true) {
                System.out.println(threadName+" is run!");
            }
            //System.out.println(threadName+" interrput flag is "+isInterrupted());
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread endThread = new UseThread("endThread");
        endThread.start();
        Thread.sleep(20);
        endThread.interrupt();

    }

}

 

 

 

package com.xiangxue.ch1.safeend;

/**
 *@author Mark老师   享学课堂 https://enjoy.ke.qq.com 
 *
 *类说明:中断Runnable类型的线程
 */
public class EndRunnable {
    
    private static class UseRunnable implements Runnable{
        
        @Override
        public void run() {

            String threadName = Thread.currentThread().getName();
            while(Thread.currentThread().isInterrupted()) {
                System.out.println(threadName+" is run!");
            }
            System.out.println(threadName+" interrput flag is "
                    +Thread.currentThread().isInterrupted());
        }            
    }

    public static void main(String[] args) throws InterruptedException {
        UseRunnable useRunnable = new UseRunnable();
        Thread endThread = new Thread(useRunnable,"endThread");
        endThread.start();
        Thread.sleep(20);
        endThread.interrupt();
    }

}

 

 

 

 

Java里的线程再多一点点认识

线程常用方法和线程的状态

线程只有5种状态。整个生命周期就是这几种状态的切换。

run()start() run方法就是普通对象的普通方法,只有调用了start()后,Java才会将线程对象和操作系统中实际的线程进行映射,再来执行run方法。

yield() :让出cpu的执行权,将线程从运行转到可运行状态,但是下个时间片,该线程依然有可能被再次选中运行。

技术图片

 

 

 

 

线程的优先级

取值为1~10,缺省为5,但线程的优先级不可靠,不建议作为线程开发时候的手段

守护线程

和主线程共死,finally不能保证一定执行

 

package com.xiangxue.ch1;

import java.util.concurrent.ExecutionException;

/**
 * @author
 *
 *  类说明:守护线程的使用和守护线程中的finally语句块
 */
public class DaemonThread {
    
    private static class UseThread extends Thread {
        @Override
        public void run() {
            try {
                while (!isInterrupted()) {
                    System.out.println(Thread.currentThread().getName()
                            + " I am extends Thread.");
                }
                System.out.println(Thread.currentThread().getName() 
                        + " interrupt flag is " + isInterrupted());
            } finally {
                System.out.println("...........finally");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException, 
        ExecutionException {
        UseThread useThread = new UseThread();
        useThread.setDaemon(true);
        useThread.start();
        Thread.sleep(5);
        //useThread.interrupt(); 
    }
}

 

 

 

线程间的共享

synchronized内置锁

对象锁,锁的是类的对象实例。

类锁 ,锁的是每个类的的Class对象,每个类的的Class对象在一个虚拟机中只有一个,所以类锁也只有一个。

举例:

 

package com.xiangxue.ch1.syn;

import com.xiangxue.tools.SleepTools;

/**
 *@author
 *Synchronized 关键字
 *类说明:演示对象锁和类锁
 */
public class SynClzAndInst {
    
    //使用类锁的线程
    private static class SynClass extends Thread{
        @Override
        public void run() {
            System.out.println("TestClass is running...");
            synClass();
        }
    }

    //使用对象锁的线程
    private static class InstanceSyn implements Runnable{
        private SynClzAndInst synClzAndInst;

        public InstanceSyn(SynClzAndInst synClzAndInst) {
            this.synClzAndInst = synClzAndInst;
        }

        @Override
        public void run() {
            System.out.println("TestInstance is running..."+synClzAndInst);
            synClzAndInst.instance();
        }
    }

  //使用对象锁的线程
    private static class Instance2Syn implements Runnable{
        private SynClzAndInst synClzAndInst;

        public Instance2Syn(SynClzAndInst synClzAndInst) {
            this.synClzAndInst = synClzAndInst;
        }
        @Override
        public void run() {
            System.out.println("TestInstance2 is running..."+synClzAndInst);
            synClzAndInst.instance2();
        }
    }

    //锁对象
    private synchronized void instance(){
        SleepTools.second(3);
        System.out.println("synInstance is going..."+this.toString());
        SleepTools.second(3);
        System.out.println("synInstance ended "+this.toString());
    }
    
    //锁对象
    private synchronized void instance2(){
        SleepTools.second(3);
        System.out.println("synInstance2 is going..."+this.toString());
        SleepTools.second(3);
        System.out.println("synInstance2 ended "+this.toString());
    }

    //类锁,实际是锁类的class对象
    private static synchronized void synClass(){
        SleepTools.second(1);
        System.out.println("synClass going...");
        SleepTools.second(1);
        System.out.println("synClass end");
    }

    public static void main(String[] args) {
        SynClzAndInst synClzAndInst = new SynClzAndInst();
        Thread t1 = new Thread(new InstanceSyn(synClzAndInst));
        
        //SynClzAndInst synClzAndInst2 = new SynClzAndInst();
        //Thread t2 = new Thread(new Instance2Syn(synClzAndInst));
        
        t1.start();
        //t2.start();
        
        SynClass synClass = new SynClass();
        synClass.start();
        SleepTools.second(1);
    }
}

 

 

 

volatile关键字

适合于只有一个线程写,多个线程读的场景,因为它只能确保可见性。

 

package com.xiangxue.ch1.vola;

import com.xiangxue.tools.SleepTools;

/**
 *@author
 *
 *类说明:演示violate无法提供操作的原子性
 */
public class VolatileUnsafe {
    
    private static class VolatileVar implements Runnable {

        private volatile int a = 0;
        
        @Override
        public void run() {
            String threadName = Thread.currentThread().getName();
            a = a++;
            System.out.println(threadName+":======"+a);
            SleepTools.ms(100);
            a = a+1;
            System.out.println(threadName+":======"+a);
        }
    }
    
    public static void main(String[] args) {

        VolatileVar v = new VolatileVar();

        Thread t1 = new Thread(v);
        Thread t2 = new Thread(v);
        Thread t3 = new Thread(v);
        Thread t4 = new Thread(v);
        t1.start();
        t2.start();
        t3.start();
        t4.start();
    }

}

 

 

 

ThreadLocal

线程变量。可以理解为是个map,类型 Map<Thread,Integer>

 

package com.xiangxue.ch1;

/**
 *@author Mark老师   享学课堂 https://enjoy.ke.qq.com 
 *
 *类说明:演示ThreadLocal的使用
 */
public class UseThreadLocal {
    
    //可以理解为 一个map,类型 Map<Thread,Integer>
    static ThreadLocal<Integer> threadLaocl = new ThreadLocal<Integer>(){
        @Override
        protected Integer initialValue() {
            return 1;
        }
    };

    /**
     * 运行3个线程
     */
    public void StartThreadArray(){
        Thread[] runs = new Thread[3];
        for(int i=0;i<runs.length;i++){
            runs[i]=new Thread(new TestThread(i));
        }
        for(int i=0;i<runs.length;i++){
            runs[i].start();
        }
    }
    
    /**
     *类说明:测试线程,线程的工作是将ThreadLocal变量的值变化,并写回,看看线程之间是否会互相影响
     */
    public static class TestThread implements Runnable{
        int id;
        public TestThread(int id){
            this.id = id;
        }
        public void run() {
            System.out.println(Thread.currentThread().getName()+":start");
            Integer s = threadLaocl.get();//获得变量的值
            s = s+id;
            threadLaocl.set(s);
            System.out.println(Thread.currentThread().getName()+":"
            +threadLaocl.get());
            //threadLaocl.remove();
        }
    }

    public static void main(String[] args){
        UseThreadLocal test = new UseThreadLocal();
        test.StartThreadArray();
    }
}

 

 

 

 

 

 

线程间协作

轮询难以保证及时性资源开销很大

等待和通知

wait()    对象上的方法

 

notify/notifyAll  对象上的方法

 

等待和通知的标准范式

等待方

1、 获取对象的锁;

2、 循环里判断条件是否满足,不满足调用wait方法,

3、 条件满足执行业务逻辑

通知方来说

1、 获取对象的锁;

2、 改变条件

3、 通知所有等待在对象的线程

 

 

notifynotifyAll应该用谁?

应该尽量使用notifyAll,使用notify因为有可能发生信号丢失的的情况

等待超时模式实现一个连接池

假设  等待时间时长为T,当前时间now+T以后超时

 

long  overtime = now+T;

long remain = T;//等待的持续时间

while(result不满足条件&& remain>0){

wait(remain);

remain = overtime – now;//等待剩下的持续时间

}

return result;

package com.xiangxue.ch1.pool;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

import com.xiangxue.tools.SleepTools;

/**
 *@author Mark老师   享学课堂 https://enjoy.ke.qq.com 
 *
 *类说明:实现了数据库连接的实现
 */
public class SqlConnectImpl implements Connection{
    
    /*拿一个数据库连接*/
    public static final Connection fetchConnection(){
        return new SqlConnectImpl();
    }

    @Override
    public boolean isWrapperFor(Class<?> arg0) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public <T> T unwrap(Class<T> arg0) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void abort(Executor arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void clearWarnings() throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void close() throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void commit() throws SQLException {
        SleepTools.ms(70);
    }

    @Override
    public Array createArrayOf(String arg0, Object[] arg1) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Blob createBlob() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Clob createClob() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public NClob createNClob() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public SQLXML createSQLXML() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Statement createStatement() throws SQLException {
        SleepTools.ms(1);
        return null;
    }

    @Override
    public Statement createStatement(int arg0, int arg1) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Statement createStatement(int arg0, int arg1, int arg2) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Struct createStruct(String arg0, Object[] arg1) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean getAutoCommit() throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public String getCatalog() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Properties getClientInfo() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public String getClientInfo(String arg0) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getHoldability() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public DatabaseMetaData getMetaData() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getNetworkTimeout() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getSchema() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int getTransactionIsolation() throws SQLException {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public Map<String, Class<?>> getTypeMap() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public SQLWarning getWarnings() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public boolean isClosed() throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isReadOnly() throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public boolean isValid(int arg0) throws SQLException {
        // TODO Auto-generated method stub
        return false;
    }

    @Override
    public String nativeSQL(String arg0) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public CallableStatement prepareCall(String arg0) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public CallableStatement prepareCall(String arg0, int arg1, int arg2) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public CallableStatement prepareCall(String arg0, int arg1, int arg2, int arg3) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String arg0) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String arg0, int arg1) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String arg0, int[] arg1) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String arg0, String[] arg1) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String arg0, int arg1, int arg2) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public PreparedStatement prepareStatement(String arg0, int arg1, int arg2, int arg3) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void releaseSavepoint(Savepoint arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void rollback() throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void rollback(Savepoint arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setAutoCommit(boolean arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setCatalog(String arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setClientInfo(Properties arg0) throws SQLClientInfoException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setClientInfo(String arg0, String arg1) throws SQLClientInfoException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setHoldability(int arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setNetworkTimeout(Executor arg0, int arg1) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setReadOnly(boolean arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public Savepoint setSavepoint() throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Savepoint setSavepoint(String arg0) throws SQLException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public void setSchema(String arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setTransactionIsolation(int arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }

    @Override
    public void setTypeMap(Map<String, Class<?>> arg0) throws SQLException {
        // TODO Auto-generated method stub
        
    }
    
}
package com.xiangxue.ch1.pool;

import java.sql.Connection;
import java.util.LinkedList;

/**
 *@author Mark老师   享学课堂 https://enjoy.ke.qq.com 
 *
 *类说明:实现一个数据库的连接池
 */
public class DBPool {
    
    //数据库池的容器
    private static LinkedList<Connection> pool = new LinkedList<>();
    
    public DBPool(int initalSize) {
        if(initalSize>0) {
            for(int i=0;i<initalSize;i++) {
                pool.addLast(SqlConnectImpl.fetchConnection());
            }
        }
    }
    
    //在mills时间内还拿不到数据库连接,返回一个null
    public Connection fetchConn(long mills) throws InterruptedException {
        synchronized (pool) {
            if (mills<0) {
                while(pool.isEmpty()) {
                    pool.wait();
                }
                return pool.removeFirst();
            }else {
                long overtime = System.currentTimeMillis()+mills;
                long remain = mills;
                while(pool.isEmpty()&&remain>0) {
                    pool.wait(remain);//注意:等待remain时间后,执行wait,wait会释放锁
                    remain = overtime - System.currentTimeMillis();
                }
                Connection result  = null;
                if(!pool.isEmpty()) {
                    result = pool.removeFirst();
                }
                return result;
            }
        }
    }
    
    //放回数据库连接
    public void releaseConn(Connection conn) {
        if(conn!=null) {
            synchronized (pool) {
                pool.addLast(conn);
                pool.notifyAll();
            }
        }
    }

 
}
package com.xiangxue.ch1.pool;

import java.sql.Connection;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

/**
 *@author Mark老师   享学课堂 https://enjoy.ke.qq.com 
 *
 *类说明:
 */
public class DBPoolTest {
    static DBPool pool  = new DBPool(10);
    // 控制器:控制main线程将会等待所有Woker结束后才能继续执行
    static CountDownLatch end;

    public static void main(String[] args) throws Exception {
        // 线程数量
        int threadCount = 50;
        end = new CountDownLatch(threadCount);
        int count = 20;//每个线程的操作次数
        AtomicInteger got = new AtomicInteger();//计数器:统计可以拿到连接的线程
        AtomicInteger notGot = new AtomicInteger();//计数器:统计没有拿到连接的线程
        for (int i = 0; i < threadCount; i++) {
            Thread thread = new Thread(new Worker(count, got, notGot), 
                    "worker_"+i);
            thread.start();
        }
        end.await();// main线程在此处等待
        System.out.println("总共尝试了: " + (threadCount * count));
        System.out.println("拿到连接的次数:  " + got);
        System.out.println("没能连接的次数: " + notGot);
    }

    static class Worker implements Runnable {
        int           count;
        AtomicInteger got;
        AtomicInteger notGot;

        public Worker(int count, AtomicInteger got,
                               AtomicInteger notGot) {
            this.count = count;
            this.got = got;
            this.notGot = notGot;
        }

        public void run() {
            while (count > 0) {
                try {
                    // 从线程池中获取连接,如果1000ms内无法获取到,将会返回null
                    // 分别统计连接获取的数量got和未获取到的数量notGot
                    Connection connection = pool.fetchConn(1000);
                    if (connection != null) {
                        try {
                            connection.createStatement();
                            connection.commit();
                        } finally {
                            pool.releaseConn(connection);
                            got.incrementAndGet();
                        }
                    } else {
                        notGot.incrementAndGet();
                        System.out.println(Thread.currentThread().getName()
                                +"等待超时!");
                    }
                } catch (Exception ex) {
                } finally {
                    count--;
                }
            }
            end.countDown();
        }
    }
}

 

join()方法

面试点

线程A,执行了线程Bjoin方法,线程A必须要等待B执行完成了以后,线程A才能继续自己的工作

 

调用yield() sleep()wait()notify()等方法对锁有何影响? 

技术图片

 

 

面试点

线程在执行yield()以后持有的锁是不释放的,Cpu也是会有可能选择当前线程进行执行。

sleep()方法被调用以后,持有的锁是不释放的,休眠的时候CPU不会选择改线程进行执行。

调用方法之前必须要持有锁调用了wait()方法以后,锁就会被释放,当wait方法返回的时候,线程会重新持有锁

调用方法之前必须要持有锁,调用notify()方法本身不会释放锁的

 

 

一般将notify,notifyall放在同步代码块的最后。
/* 变化公里数,然后通知处于wait状态并需要处理公里数的线程进行业务处理*/ public synchronized void changeKm(){ this.km = 101; notifyAll(); //其他的业务代码 这样写不合理 }

 

以上是关于并发编程的主要内容,如果未能解决你的问题,请参考以下文章

golang代码片段(摘抄)

《java并发编程实战》

Java并发编程实战 04死锁了怎么办?

Java并发编程实战 04死锁了怎么办?

Java编程思想之二十 并发

golang goroutine例子[golang并发代码片段]